]> git.cameronkatri.com Git - pw-darwin.git/blobdiff - pw/pw_conf.c
Use arc4random_uniform() to avoid "modulo bias"
[pw-darwin.git] / pw / pw_conf.c
index 4a86c900f692aaab82ab1fd61e33dfcf79418bcb..51672b9ae4932d01faca483a085a773209936ef1 100644 (file)
@@ -1,26 +1,20 @@
 /*-
- * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
- * All rights reserved.
+ * Copyright (C) 1996
+ *     David L. Nugent.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer as
- *    the first lines of this file unmodified.
+ *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by David L. Nugent.
- * 4. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- *
- *     $Id$
  */
 
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
 #include <string.h>
 #include <ctype.h>
 #include <fcntl.h>
@@ -45,10 +42,12 @@ enum {
        _UC_DEFAULTPWD,
        _UC_REUSEUID,
        _UC_REUSEGID,
+       _UC_NISPASSWD,
        _UC_DOTDIR,
        _UC_NEWMAIL,
        _UC_LOGFILE,
        _UC_HOMEROOT,
+       _UC_HOMEMODE,
        _UC_SHELLPATH,
        _UC_SHELLS,
        _UC_DEFAULTSHELL,
@@ -69,12 +68,8 @@ static char     bourne_shell[] = "sh";
 static char    *system_shells[_UC_MAXSHELLS] =
 {
        bourne_shell,
-       "csh"
-};
-
-static char    *default_groups[_UC_MAXGROUPS] =
-{
-       NULL
+       "csh",
+       "tcsh"
 };
 
 static char const *booltrue[] =
@@ -91,20 +86,23 @@ static struct userconf config =
        0,                      /* Default password for new users? (nologin) */
        0,                      /* Reuse uids? */
        0,                      /* Reuse gids? */
+       NULL,                   /* NIS version of the passwd file */
        "/usr/share/skel",      /* Where to obtain skeleton files */
        NULL,                   /* Mail to send to new accounts */
        "/var/log/userlog",     /* Where to log changes */
        "/home",                /* Where to create home directory */
+       0777,                   /* Home directory perms, modified by umask */
        "/bin",                 /* Where shells are located */
        system_shells,          /* List of shells (first is default) */
        bourne_shell,           /* Default shell */
        NULL,                   /* Default group name */
-       default_groups,         /* Default (additional) groups */
+       NULL,                   /* Default (additional) groups */
        NULL,                   /* Default login class */
        1000, 32000,            /* Allowed range of uids */
        1000, 32000,            /* Allowed range of gids */
        0,                      /* Days until account expires */
-       0                       /* Days until password expires */
+       0,                      /* Days until password expires */
+       0                       /* size of default_group array */
 };
 
 static char const *comments[_UC_FIELDS] =
@@ -113,12 +111,14 @@ static char const *comments[_UC_FIELDS] =
        "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
        "\n# Reuse gaps in uid sequence? (yes or no)\n",
        "\n# Reuse gaps in gid sequence? (yes or no)\n",
+       "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
        "\n# Obtain default dotfiles from this directory\n",
        "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
        "\n# Log add/change/remove information in this file\n",
        "\n# Root directory in which $HOME directory is created\n",
+       "\n# Mode for the new $HOME directory, will be modified by umask\n",
        "\n# Colon separated list of directories containing valid shells\n",
-       "\n# Space separated list of available shells (without paths)\n",
+       "\n# Comma separated list of available shells (without paths)\n",
        "\n# Default shell (without path)\n",
        "\n# Default group (leave blank for new group per user)\n",
        "\n# Extra groups for new users\n",
@@ -137,10 +137,12 @@ static char const *kwds[] =
        "defaultpasswd",
        "reuseuids",
        "reusegids",
+       "nispasswd",
        "skeleton",
        "newmail",
        "logfile",
        "home",
+       "homemode",
        "shellpath",
        "shells",
        "defaultshell",
@@ -218,126 +220,155 @@ newstr(char const * p)
        return q;
 }
 
+#define LNBUFSZ 1024
+
 
 struct userconf *
 read_userconfig(char const * file)
 {
        FILE           *fp;
 
+       extendarray(&config.groups, &config.numgroups, 200);
+       memset(config.groups, 0, config.numgroups * sizeof(char *));
        if (file == NULL)
                file = _PATH_PW_CONF;
        if ((fp = fopen(file, "r")) != NULL) {
-               char            buf[_UC_MAXLINE];
-
-               while (fgets(buf, sizeof buf, fp) != NULL) {
-                       char           *p = strchr(buf, '\n');
-
-                       if (p == NULL) {        /* Line too long */
-                               int             ch;
+               int         buflen = LNBUFSZ;
+               char       *buf = malloc(buflen);
+
+       nextline:
+               while (fgets(buf, buflen, fp) != NULL) {
+                       char           *p;
+
+                       while ((p = strchr(buf, '\n')) == NULL) {
+                               int       l;
+                               if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
+                                       int     ch;
+                                       while ((ch = fgetc(fp)) != '\n' && ch != EOF);
+                                       goto nextline;  /* Ignore it */
+                               }
+                               l = strlen(buf);
+                               if (fgets(buf + l, buflen - l, fp) == NULL)
+                                       break;  /* Unterminated last line */
+                       }
 
-                               while ((ch = fgetc(fp)) != '\n' && ch != EOF);
-                       } else {
+                       if (p != NULL)
                                *p = '\0';
-                               if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
-                                       static char const toks[] = " \t\r\n,=";
-                                       char           *q = strtok(NULL, toks);
-                                       int             i = 0;
 
-                                       while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
-                                               ++i;
+                       if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
+                               static char const toks[] = " \t\r\n,=";
+                               char           *q = strtok(NULL, toks);
+                               int             i = 0;
+                               mode_t          *modeset;
+
+                               while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
+                                       ++i;
 #if debugging
-                                       if (i == _UC_FIELDS)
-                                               printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
-                                       else
-                                               printf("Got kwd[%s]=%s\n", p, q);
+                               if (i == _UC_FIELDS)
+                                       printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
+                               else
+                                       printf("Got kwd[%s]=%s\n", p, q);
 #endif
-                                       switch (i) {
-                                       case _UC_DEFAULTPWD:
-                                               config.default_password = boolean_val(q, 1);
-                                               break;
-                                       case _UC_REUSEUID:
-                                               config.reuse_uids = boolean_val(q, 0);
-                                               break;
-                                       case _UC_REUSEGID:
-                                               config.reuse_gids = boolean_val(q, 0);
-                                               break;
-                                       case _UC_DOTDIR:
-                                               config.dotdir = (q == NULL || !boolean_val(q, 1))
-                                                       ? NULL : newstr(q);
-                                               break;
-                                       case _UC_NEWMAIL:
-                                               config.newmail = (q == NULL || !boolean_val(q, 1))
-                                                       ? NULL : newstr(q);
-                                               break;
-                                       case _UC_LOGFILE:
-                                               config.logfile = (q == NULL || !boolean_val(q, 1))
-                                                       ? NULL : newstr(q);
-                                               break;
-                                       case _UC_HOMEROOT:
-                                               config.home = (q == NULL || !boolean_val(q, 1))
-                                                       ? "/home" : newstr(q);
-                                               break;
-                                       case _UC_SHELLPATH:
-                                               config.shelldir = (q == NULL || !boolean_val(q, 1))
-                                                       ? "/bin" : newstr(q);
-                                               break;
-                                       case _UC_SHELLS:
-                                               for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
-                                                       system_shells[i] = newstr(q);
-                                               if (i > 0)
-                                                       while (i < _UC_MAXSHELLS)
-                                                               system_shells[i++] = NULL;
-                                               break;
-                                       case _UC_DEFAULTSHELL:
-                                               config.shell_default = (q == NULL || !boolean_val(q, 1))
-                                                       ? (char *) bourne_shell : newstr(q);
-                                               break;
-                                       case _UC_DEFAULTGROUP:
-                                               config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
-                                                       ? NULL : newstr(q);
-                                               break;
-                                       case _UC_EXTRAGROUPS:
-                                               for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks))
-                                                       default_groups[i] = newstr(q);
-                                               if (i > 0)
-                                                       while (i < _UC_MAXGROUPS)
-                                                               default_groups[i++] = NULL;
-                                               break;
-                                       case _UC_DEFAULTCLASS:
-                                               config.default_class = (q == NULL || !boolean_val(q, 1))
-                                                       ? NULL : newstr(q);
-                                               break;
-                                       case _UC_MINUID:
-                                               if ((q = unquote(q)) != NULL && isdigit(*q))
-                                                       config.min_uid = (uid_t) atol(q);
-                                               break;
-                                       case _UC_MAXUID:
-                                               if ((q = unquote(q)) != NULL && isdigit(*q))
-                                                       config.max_uid = (uid_t) atol(q);
-                                               break;
-                                       case _UC_MINGID:
-                                               if ((q = unquote(q)) != NULL && isdigit(*q))
-                                                       config.min_gid = (gid_t) atol(q);
-                                               break;
-                                       case _UC_MAXGID:
-                                               if ((q = unquote(q)) != NULL && isdigit(*q))
-                                                       config.max_gid = (gid_t) atol(q);
-                                               break;
-                                       case _UC_EXPIRE:
-                                               if ((q = unquote(q)) != NULL && isdigit(*q))
-                                                       config.expire_days = atoi(q);
-                                               break;
-                                       case _UC_PASSWORD:
-                                               if ((q = unquote(q)) != NULL && isdigit(*q))
-                                                       config.password_days = atoi(q);
-                                               break;
-                                       case _UC_FIELDS:
-                                       case _UC_NONE:
-                                               break;
+                               switch (i) {
+                               case _UC_DEFAULTPWD:
+                                       config.default_password = boolean_val(q, 1);
+                                       break;
+                               case _UC_REUSEUID:
+                                       config.reuse_uids = boolean_val(q, 0);
+                                       break;
+                               case _UC_REUSEGID:
+                                       config.reuse_gids = boolean_val(q, 0);
+                                       break;
+                               case _UC_NISPASSWD:
+                                       config.nispasswd = (q == NULL || !boolean_val(q, 1))
+                                               ? NULL : newstr(q);
+                                       break;
+                               case _UC_DOTDIR:
+                                       config.dotdir = (q == NULL || !boolean_val(q, 1))
+                                               ? NULL : newstr(q);
+                                       break;
+                               case _UC_NEWMAIL:
+                                       config.newmail = (q == NULL || !boolean_val(q, 1))
+                                               ? NULL : newstr(q);
+                                       break;
+                               case _UC_LOGFILE:
+                                       config.logfile = (q == NULL || !boolean_val(q, 1))
+                                               ? NULL : newstr(q);
+                                       break;
+                               case _UC_HOMEROOT:
+                                       config.home = (q == NULL || !boolean_val(q, 1))
+                                               ? "/home" : newstr(q);
+                                       break;
+                               case _UC_HOMEMODE:
+                                       modeset = setmode(q);
+                                       config.homemode = (q == NULL || !boolean_val(q, 1))
+                                               ? 0777 : getmode(modeset, 0777);
+                                       free(modeset);
+                                       break;
+                               case _UC_SHELLPATH:
+                                       config.shelldir = (q == NULL || !boolean_val(q, 1))
+                                               ? "/bin" : newstr(q);
+                                       break;
+                               case _UC_SHELLS:
+                                       for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
+                                               system_shells[i] = newstr(q);
+                                       if (i > 0)
+                                               while (i < _UC_MAXSHELLS)
+                                                       system_shells[i++] = NULL;
+                                       break;
+                               case _UC_DEFAULTSHELL:
+                                       config.shell_default = (q == NULL || !boolean_val(q, 1))
+                                               ? (char *) bourne_shell : newstr(q);
+                                       break;
+                               case _UC_DEFAULTGROUP:
+                                       q = unquote(q);
+                                       config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
+                                               ? NULL : newstr(q);
+                                       break;
+                               case _UC_EXTRAGROUPS:
+                                       for (i = 0; q != NULL; q = strtok(NULL, toks)) {
+                                               if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
+                                                       config.groups[i++] = newstr(q);
                                        }
+                                       if (i > 0)
+                                               while (i < config.numgroups)
+                                                       config.groups[i++] = NULL;
+                                       break;
+                               case _UC_DEFAULTCLASS:
+                                       config.default_class = (q == NULL || !boolean_val(q, 1))
+                                               ? NULL : newstr(q);
+                                       break;
+                               case _UC_MINUID:
+                                       if ((q = unquote(q)) != NULL && isdigit(*q))
+                                               config.min_uid = (uid_t) atol(q);
+                                       break;
+                               case _UC_MAXUID:
+                                       if ((q = unquote(q)) != NULL && isdigit(*q))
+                                               config.max_uid = (uid_t) atol(q);
+                                       break;
+                               case _UC_MINGID:
+                                       if ((q = unquote(q)) != NULL && isdigit(*q))
+                                               config.min_gid = (gid_t) atol(q);
+                                       break;
+                               case _UC_MAXGID:
+                                       if ((q = unquote(q)) != NULL && isdigit(*q))
+                                               config.max_gid = (gid_t) atol(q);
+                                       break;
+                               case _UC_EXPIRE:
+                                       if ((q = unquote(q)) != NULL && isdigit(*q))
+                                               config.expire_days = atoi(q);
+                                       break;
+                               case _UC_PASSWORD:
+                                       if ((q = unquote(q)) != NULL && isdigit(*q))
+                                               config.password_days = atoi(q);
+                                       break;
+                               case _UC_FIELDS:
+                               case _UC_NONE:
+                                       break;
                                }
                        }
                }
+               free(buf);
                fclose(fp);
        }
        return &config;
@@ -359,7 +390,8 @@ write_userconfig(char const * file)
                        close(fd);
                else {
                        int             i, j, k;
-                       char            buf[_UC_MAXLINE];
+                       int             len = LNBUFSZ;
+                       char           *buf = malloc(len);
 
                        for (i = _UC_NONE; i < _UC_FIELDS; i++) {
                                int             quote = 1;
@@ -376,6 +408,10 @@ write_userconfig(char const * file)
                                case _UC_REUSEGID:
                                        val = boolean_str(config.reuse_gids);
                                        break;
+                               case _UC_NISPASSWD:
+                                       val = config.nispasswd ? config.nispasswd : "";
+                                       quote = 0;
+                                       break;
                                case _UC_DOTDIR:
                                        val = config.dotdir ? config.dotdir : boolean_str(0);
                                        break;
@@ -388,12 +424,24 @@ write_userconfig(char const * file)
                                case _UC_HOMEROOT:
                                        val = config.home;
                                        break;
+                               case _UC_HOMEMODE:
+                                       sprintf(buf, "%04o", config.homemode);
+                                       quote = 0;
+                                       break;
                                case _UC_SHELLPATH:
                                        val = config.shelldir;
                                        break;
                                case _UC_SHELLS:
-                                       for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++)
-                                               k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]);
+                                       for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
+                                               char    lbuf[64];
+                                               int     l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
+                                               if (l < 0)
+                                                       l = 0;
+                                               if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
+                                                       strcpy(buf + k, lbuf);
+                                                       k += l;
+                                               }
+                                       }
                                        quote = 0;
                                        break;
                                case _UC_DEFAULTSHELL:
@@ -403,8 +451,17 @@ write_userconfig(char const * file)
                                        val = config.default_group ? config.default_group : "";
                                        break;
                                case _UC_EXTRAGROUPS:
-                                       for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++)
-                                               k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]);
+                                       extendarray(&config.groups, &config.numgroups, 200);
+                                       for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
+                                               char    lbuf[64];
+                                               int     l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
+                                               if (l < 0)
+                                                       l = 0;
+                                               if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
+                                                       strcpy(buf + k, lbuf);
+                                                       k +=  l;
+                                               }
+                                       }
                                        quote = 0;
                                        break;
                                case _UC_DEFAULTCLASS:
@@ -451,6 +508,7 @@ write_userconfig(char const * file)
 #endif
                                }
                        }
+                       free(buf);
                        return fclose(fp) != EOF;
                }
        }