pw_conf.c revision 20253
120253Sjoerg/*- 220253Sjoerg * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>. 320253Sjoerg * All rights reserved. 420253Sjoerg * 520253Sjoerg * Redistribution and use in source and binary forms, with or without 620253Sjoerg * modification, are permitted provided that the following conditions 720253Sjoerg * are met: 820253Sjoerg * 1. Redistributions of source code must retain the above copyright 920253Sjoerg * notice, this list of conditions and the following disclaimer as 1020253Sjoerg * the first lines of this file unmodified. 1120253Sjoerg * 2. Redistributions in binary form must reproduce the above copyright 1220253Sjoerg * notice, this list of conditions and the following disclaimer in the 1320253Sjoerg * documentation and/or other materials provided with the distribution. 1420253Sjoerg * 3. All advertising materials mentioning features or use of this software 1520253Sjoerg * must display the following acknowledgement: 1620253Sjoerg * This product includes software developed by David L. Nugent. 1720253Sjoerg * 4. The name of the author may not be used to endorse or promote products 1820253Sjoerg * derived from this software without specific prior written permission. 1920253Sjoerg * 2020253Sjoerg * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND 2120253Sjoerg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2220253Sjoerg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2320253Sjoerg * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE 2420253Sjoerg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2520253Sjoerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2620253Sjoerg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2720253Sjoerg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2820253Sjoerg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2920253Sjoerg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3020253Sjoerg * SUCH DAMAGE. 3120253Sjoerg * 3220253Sjoerg * $Id$ 3320253Sjoerg */ 3420253Sjoerg 3520253Sjoerg#include <string.h> 3620253Sjoerg#include <ctype.h> 3720253Sjoerg#include <fcntl.h> 3820253Sjoerg 3920253Sjoerg#include "pw.h" 4020253Sjoerg 4120253Sjoerg#define debugging 0 4220253Sjoerg 4320253Sjoergenum { 4420253Sjoerg _UC_NONE, 4520253Sjoerg _UC_DEFAULTPWD, 4620253Sjoerg _UC_REUSEUID, 4720253Sjoerg _UC_REUSEGID, 4820253Sjoerg _UC_DOTDIR, 4920253Sjoerg _UC_NEWMAIL, 5020253Sjoerg _UC_LOGFILE, 5120253Sjoerg _UC_HOMEROOT, 5220253Sjoerg _UC_SHELLPATH, 5320253Sjoerg _UC_SHELLS, 5420253Sjoerg _UC_DEFAULTSHELL, 5520253Sjoerg _UC_DEFAULTGROUP, 5620253Sjoerg _UC_EXTRAGROUPS, 5720253Sjoerg _UC_DEFAULTCLASS, 5820253Sjoerg _UC_MINUID, 5920253Sjoerg _UC_MAXUID, 6020253Sjoerg _UC_MINGID, 6120253Sjoerg _UC_MAXGID, 6220253Sjoerg _UC_EXPIRE, 6320253Sjoerg _UC_PASSWORD, 6420253Sjoerg _UC_FIELDS 6520253Sjoerg}; 6620253Sjoerg 6720253Sjoergstatic char bourne_shell[] = "sh"; 6820253Sjoerg 6920253Sjoergstatic char *system_shells[_UC_MAXSHELLS] = 7020253Sjoerg{ 7120253Sjoerg bourne_shell, 7220253Sjoerg "csh" 7320253Sjoerg}; 7420253Sjoerg 7520253Sjoergstatic char *default_groups[_UC_MAXGROUPS] = 7620253Sjoerg{ 7720253Sjoerg NULL 7820253Sjoerg}; 7920253Sjoerg 8020253Sjoergstatic char const *booltrue[] = 8120253Sjoerg{ 8220253Sjoerg "yes", "true", "1", "on", NULL 8320253Sjoerg}; 8420253Sjoergstatic char const *boolfalse[] = 8520253Sjoerg{ 8620253Sjoerg "no", "false", "0", "off", NULL 8720253Sjoerg}; 8820253Sjoerg 8920253Sjoergstatic struct userconf config = 9020253Sjoerg{ 9120253Sjoerg 0, /* Default password for new users? (nologin) */ 9220253Sjoerg 0, /* Reuse uids? */ 9320253Sjoerg 0, /* Reuse gids? */ 9420253Sjoerg "/usr/share/skel", /* Where to obtain skeleton files */ 9520253Sjoerg NULL, /* Mail to send to new accounts */ 9620253Sjoerg "/var/log/userlog", /* Where to log changes */ 9720253Sjoerg "/home", /* Where to create home directory */ 9820253Sjoerg "/bin", /* Where shells are located */ 9920253Sjoerg system_shells, /* List of shells (first is default) */ 10020253Sjoerg bourne_shell, /* Default shell */ 10120253Sjoerg NULL, /* Default group name */ 10220253Sjoerg default_groups, /* Default (additional) groups */ 10320253Sjoerg NULL, /* Default login class */ 10420253Sjoerg 1000, 32000, /* Allowed range of uids */ 10520253Sjoerg 1000, 32000, /* Allowed range of gids */ 10620253Sjoerg 0, /* Days until account expires */ 10720253Sjoerg 0 /* Days until password expires */ 10820253Sjoerg}; 10920253Sjoerg 11020253Sjoergstatic char const *comments[_UC_FIELDS] = 11120253Sjoerg{ 11220253Sjoerg "#\n# pw.conf - user/group configuration defaults\n#\n", 11320253Sjoerg "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 11420253Sjoerg "\n# Reuse gaps in uid sequence? (yes or no)\n", 11520253Sjoerg "\n# Reuse gaps in gid sequence? (yes or no)\n", 11620253Sjoerg "\n# Obtain default dotfiles from this directory\n", 11720253Sjoerg "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 11820253Sjoerg "\n# Log add/change/remove information in this file\n", 11920253Sjoerg "\n# Root directory in which $HOME directory is created\n", 12020253Sjoerg "\n# Colon separated list of directories containing valid shells\n", 12120253Sjoerg "\n# Space separated list of available shells (without paths)\n", 12220253Sjoerg "\n# Default shell (without path)\n", 12320253Sjoerg "\n# Default group (leave blank for new group per user)\n", 12420253Sjoerg "\n# Extra groups for new users\n", 12520253Sjoerg "\n# Default login class for new users\n", 12620253Sjoerg "\n# Range of valid default user ids\n", 12720253Sjoerg NULL, 12820253Sjoerg "\n# Range of valid default group ids\n", 12920253Sjoerg NULL, 13020253Sjoerg "\n# Days after which account expires (0=disabled)\n", 13120253Sjoerg "\n# Days after which password expires (0=disabled)\n" 13220253Sjoerg}; 13320253Sjoerg 13420253Sjoergstatic char const *kwds[] = 13520253Sjoerg{ 13620253Sjoerg "", 13720253Sjoerg "defaultpasswd", 13820253Sjoerg "reuseuids", 13920253Sjoerg "reusegids", 14020253Sjoerg "skeleton", 14120253Sjoerg "newmail", 14220253Sjoerg "logfile", 14320253Sjoerg "home", 14420253Sjoerg "shellpath", 14520253Sjoerg "shells", 14620253Sjoerg "defaultshell", 14720253Sjoerg "defaultgroup", 14820253Sjoerg "extragroups", 14920253Sjoerg "defaultclass", 15020253Sjoerg "minuid", 15120253Sjoerg "maxuid", 15220253Sjoerg "mingid", 15320253Sjoerg "maxgid", 15420253Sjoerg "expire_days", 15520253Sjoerg "password_days", 15620253Sjoerg NULL 15720253Sjoerg}; 15820253Sjoerg 15920253Sjoergstatic char * 16020253Sjoergunquote(char const * str) 16120253Sjoerg{ 16220253Sjoerg if (str && (*str == '"' || *str == '\'')) { 16320253Sjoerg char *p = strchr(str + 1, *str); 16420253Sjoerg 16520253Sjoerg if (p != NULL) 16620253Sjoerg *p = '\0'; 16720253Sjoerg return (char *) (*++str ? str : NULL); 16820253Sjoerg } 16920253Sjoerg return (char *) str; 17020253Sjoerg} 17120253Sjoerg 17220253Sjoergint 17320253Sjoergboolean_val(char const * str, int dflt) 17420253Sjoerg{ 17520253Sjoerg if ((str = unquote(str)) != NULL) { 17620253Sjoerg int i; 17720253Sjoerg 17820253Sjoerg for (i = 0; booltrue[i]; i++) 17920253Sjoerg if (strcmp(str, booltrue[i]) == 0) 18020253Sjoerg return 1; 18120253Sjoerg for (i = 0; boolfalse[i]; i++) 18220253Sjoerg if (strcmp(str, boolfalse[i]) == 0) 18320253Sjoerg return 0; 18420253Sjoerg 18520253Sjoerg /* 18620253Sjoerg * Special cases for defaultpassword 18720253Sjoerg */ 18820253Sjoerg if (strcmp(str, "random") == 0) 18920253Sjoerg return -1; 19020253Sjoerg if (strcmp(str, "none") == 0) 19120253Sjoerg return -2; 19220253Sjoerg } 19320253Sjoerg return dflt; 19420253Sjoerg} 19520253Sjoerg 19620253Sjoergchar const * 19720253Sjoergboolean_str(int val) 19820253Sjoerg{ 19920253Sjoerg if (val == -1) 20020253Sjoerg return "random"; 20120253Sjoerg else if (val == -2) 20220253Sjoerg return "none"; 20320253Sjoerg else 20420253Sjoerg return val ? booltrue[0] : boolfalse[0]; 20520253Sjoerg} 20620253Sjoerg 20720253Sjoergchar * 20820253Sjoergnewstr(char const * p) 20920253Sjoerg{ 21020253Sjoerg char *q = NULL; 21120253Sjoerg 21220253Sjoerg if ((p = unquote(p)) != NULL) { 21320253Sjoerg int l = strlen(p) + 1; 21420253Sjoerg 21520253Sjoerg if ((q = malloc(l)) != NULL) 21620253Sjoerg memcpy(q, p, l); 21720253Sjoerg } 21820253Sjoerg return q; 21920253Sjoerg} 22020253Sjoerg 22120253Sjoerg 22220253Sjoergstruct userconf * 22320253Sjoergread_userconfig(char const * file) 22420253Sjoerg{ 22520253Sjoerg FILE *fp; 22620253Sjoerg 22720253Sjoerg if (file == NULL) 22820253Sjoerg file = _PATH_PW_CONF; 22920253Sjoerg if ((fp = fopen(file, "r")) != NULL) { 23020253Sjoerg char buf[_UC_MAXLINE]; 23120253Sjoerg 23220253Sjoerg while (fgets(buf, sizeof buf, fp) != NULL) { 23320253Sjoerg char *p = strchr(buf, '\n'); 23420253Sjoerg 23520253Sjoerg if (p == NULL) { /* Line too long */ 23620253Sjoerg int ch; 23720253Sjoerg 23820253Sjoerg while ((ch = fgetc(fp)) != '\n' && ch != EOF); 23920253Sjoerg } else { 24020253Sjoerg *p = '\0'; 24120253Sjoerg if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 24220253Sjoerg static char const toks[] = " \t\r\n,="; 24320253Sjoerg char *q = strtok(NULL, toks); 24420253Sjoerg int i = 0; 24520253Sjoerg 24620253Sjoerg while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 24720253Sjoerg ++i; 24820253Sjoerg#if debugging 24920253Sjoerg if (i == _UC_FIELDS) 25020253Sjoerg printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 25120253Sjoerg else 25220253Sjoerg printf("Got kwd[%s]=%s\n", p, q); 25320253Sjoerg#endif 25420253Sjoerg switch (i) { 25520253Sjoerg case _UC_DEFAULTPWD: 25620253Sjoerg config.default_password = boolean_val(q, 1); 25720253Sjoerg break; 25820253Sjoerg case _UC_REUSEUID: 25920253Sjoerg config.reuse_uids = boolean_val(q, 0); 26020253Sjoerg break; 26120253Sjoerg case _UC_REUSEGID: 26220253Sjoerg config.reuse_gids = boolean_val(q, 0); 26320253Sjoerg break; 26420253Sjoerg case _UC_DOTDIR: 26520253Sjoerg config.dotdir = (q == NULL || !boolean_val(q, 1)) 26620253Sjoerg ? NULL : newstr(q); 26720253Sjoerg break; 26820253Sjoerg case _UC_NEWMAIL: 26920253Sjoerg config.newmail = (q == NULL || !boolean_val(q, 1)) 27020253Sjoerg ? NULL : newstr(q); 27120253Sjoerg break; 27220253Sjoerg case _UC_LOGFILE: 27320253Sjoerg config.logfile = (q == NULL || !boolean_val(q, 1)) 27420253Sjoerg ? NULL : newstr(q); 27520253Sjoerg break; 27620253Sjoerg case _UC_HOMEROOT: 27720253Sjoerg config.home = (q == NULL || !boolean_val(q, 1)) 27820253Sjoerg ? "/home" : newstr(q); 27920253Sjoerg break; 28020253Sjoerg case _UC_SHELLPATH: 28120253Sjoerg config.shelldir = (q == NULL || !boolean_val(q, 1)) 28220253Sjoerg ? "/bin" : newstr(q); 28320253Sjoerg break; 28420253Sjoerg case _UC_SHELLS: 28520253Sjoerg for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 28620253Sjoerg system_shells[i] = newstr(q); 28720253Sjoerg if (i > 0) 28820253Sjoerg while (i < _UC_MAXSHELLS) 28920253Sjoerg system_shells[i++] = NULL; 29020253Sjoerg break; 29120253Sjoerg case _UC_DEFAULTSHELL: 29220253Sjoerg config.shell_default = (q == NULL || !boolean_val(q, 1)) 29320253Sjoerg ? (char *) bourne_shell : newstr(q); 29420253Sjoerg break; 29520253Sjoerg case _UC_DEFAULTGROUP: 29620253Sjoerg config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL) 29720253Sjoerg ? NULL : newstr(q); 29820253Sjoerg break; 29920253Sjoerg case _UC_EXTRAGROUPS: 30020253Sjoerg for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks)) 30120253Sjoerg default_groups[i] = newstr(q); 30220253Sjoerg if (i > 0) 30320253Sjoerg while (i < _UC_MAXGROUPS) 30420253Sjoerg default_groups[i++] = NULL; 30520253Sjoerg break; 30620253Sjoerg case _UC_DEFAULTCLASS: 30720253Sjoerg config.default_class = (q == NULL || !boolean_val(q, 1)) 30820253Sjoerg ? NULL : newstr(q); 30920253Sjoerg break; 31020253Sjoerg case _UC_MINUID: 31120253Sjoerg if ((q = unquote(q)) != NULL && isdigit(*q)) 31220253Sjoerg config.min_uid = (uid_t) atol(q); 31320253Sjoerg break; 31420253Sjoerg case _UC_MAXUID: 31520253Sjoerg if ((q = unquote(q)) != NULL && isdigit(*q)) 31620253Sjoerg config.max_uid = (uid_t) atol(q); 31720253Sjoerg break; 31820253Sjoerg case _UC_MINGID: 31920253Sjoerg if ((q = unquote(q)) != NULL && isdigit(*q)) 32020253Sjoerg config.min_gid = (gid_t) atol(q); 32120253Sjoerg break; 32220253Sjoerg case _UC_MAXGID: 32320253Sjoerg if ((q = unquote(q)) != NULL && isdigit(*q)) 32420253Sjoerg config.max_gid = (gid_t) atol(q); 32520253Sjoerg break; 32620253Sjoerg case _UC_EXPIRE: 32720253Sjoerg if ((q = unquote(q)) != NULL && isdigit(*q)) 32820253Sjoerg config.expire_days = atoi(q); 32920253Sjoerg break; 33020253Sjoerg case _UC_PASSWORD: 33120253Sjoerg if ((q = unquote(q)) != NULL && isdigit(*q)) 33220253Sjoerg config.password_days = atoi(q); 33320253Sjoerg break; 33420253Sjoerg case _UC_FIELDS: 33520253Sjoerg case _UC_NONE: 33620253Sjoerg break; 33720253Sjoerg } 33820253Sjoerg } 33920253Sjoerg } 34020253Sjoerg } 34120253Sjoerg fclose(fp); 34220253Sjoerg } 34320253Sjoerg return &config; 34420253Sjoerg} 34520253Sjoerg 34620253Sjoerg 34720253Sjoergint 34820253Sjoergwrite_userconfig(char const * file) 34920253Sjoerg{ 35020253Sjoerg int fd; 35120253Sjoerg 35220253Sjoerg if (file == NULL) 35320253Sjoerg file = _PATH_PW_CONF; 35420253Sjoerg 35520253Sjoerg if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { 35620253Sjoerg FILE *fp; 35720253Sjoerg 35820253Sjoerg if ((fp = fdopen(fd, "w")) == NULL) 35920253Sjoerg close(fd); 36020253Sjoerg else { 36120253Sjoerg int i, j, k; 36220253Sjoerg char buf[_UC_MAXLINE]; 36320253Sjoerg 36420253Sjoerg for (i = _UC_NONE; i < _UC_FIELDS; i++) { 36520253Sjoerg int quote = 1; 36620253Sjoerg char const *val = buf; 36720253Sjoerg 36820253Sjoerg *buf = '\0'; 36920253Sjoerg switch (i) { 37020253Sjoerg case _UC_DEFAULTPWD: 37120253Sjoerg val = boolean_str(config.default_password); 37220253Sjoerg break; 37320253Sjoerg case _UC_REUSEUID: 37420253Sjoerg val = boolean_str(config.reuse_uids); 37520253Sjoerg break; 37620253Sjoerg case _UC_REUSEGID: 37720253Sjoerg val = boolean_str(config.reuse_gids); 37820253Sjoerg break; 37920253Sjoerg case _UC_DOTDIR: 38020253Sjoerg val = config.dotdir ? config.dotdir : boolean_str(0); 38120253Sjoerg break; 38220253Sjoerg case _UC_NEWMAIL: 38320253Sjoerg val = config.newmail ? config.newmail : boolean_str(0); 38420253Sjoerg break; 38520253Sjoerg case _UC_LOGFILE: 38620253Sjoerg val = config.logfile ? config.logfile : boolean_str(0); 38720253Sjoerg break; 38820253Sjoerg case _UC_HOMEROOT: 38920253Sjoerg val = config.home; 39020253Sjoerg break; 39120253Sjoerg case _UC_SHELLPATH: 39220253Sjoerg val = config.shelldir; 39320253Sjoerg break; 39420253Sjoerg case _UC_SHELLS: 39520253Sjoerg for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) 39620253Sjoerg k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]); 39720253Sjoerg quote = 0; 39820253Sjoerg break; 39920253Sjoerg case _UC_DEFAULTSHELL: 40020253Sjoerg val = config.shell_default ? config.shell_default : bourne_shell; 40120253Sjoerg break; 40220253Sjoerg case _UC_DEFAULTGROUP: 40320253Sjoerg val = config.default_group ? config.default_group : ""; 40420253Sjoerg break; 40520253Sjoerg case _UC_EXTRAGROUPS: 40620253Sjoerg for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++) 40720253Sjoerg k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]); 40820253Sjoerg quote = 0; 40920253Sjoerg break; 41020253Sjoerg case _UC_DEFAULTCLASS: 41120253Sjoerg val = config.default_class ? config.default_class : ""; 41220253Sjoerg break; 41320253Sjoerg case _UC_MINUID: 41420253Sjoerg sprintf(buf, "%lu", (unsigned long) config.min_uid); 41520253Sjoerg quote = 0; 41620253Sjoerg break; 41720253Sjoerg case _UC_MAXUID: 41820253Sjoerg sprintf(buf, "%lu", (unsigned long) config.max_uid); 41920253Sjoerg quote = 0; 42020253Sjoerg break; 42120253Sjoerg case _UC_MINGID: 42220253Sjoerg sprintf(buf, "%lu", (unsigned long) config.min_gid); 42320253Sjoerg quote = 0; 42420253Sjoerg break; 42520253Sjoerg case _UC_MAXGID: 42620253Sjoerg sprintf(buf, "%lu", (unsigned long) config.max_gid); 42720253Sjoerg quote = 0; 42820253Sjoerg break; 42920253Sjoerg case _UC_EXPIRE: 43020253Sjoerg sprintf(buf, "%d", config.expire_days); 43120253Sjoerg quote = 0; 43220253Sjoerg break; 43320253Sjoerg case _UC_PASSWORD: 43420253Sjoerg sprintf(buf, "%d", config.password_days); 43520253Sjoerg quote = 0; 43620253Sjoerg break; 43720253Sjoerg case _UC_NONE: 43820253Sjoerg break; 43920253Sjoerg } 44020253Sjoerg 44120253Sjoerg if (comments[i]) 44220253Sjoerg fputs(comments[i], fp); 44320253Sjoerg 44420253Sjoerg if (*kwds[i]) { 44520253Sjoerg if (quote) 44620253Sjoerg fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 44720253Sjoerg else 44820253Sjoerg fprintf(fp, "%s = %s\n", kwds[i], val); 44920253Sjoerg#if debugging 45020253Sjoerg printf("WROTE: %s = %s\n", kwds[i], val); 45120253Sjoerg#endif 45220253Sjoerg } 45320253Sjoerg } 45420253Sjoerg return fclose(fp) != EOF; 45520253Sjoerg } 45620253Sjoerg } 45720253Sjoerg return 0; 45820253Sjoerg} 459