pw_conf.c revision 70133
1204076Spjd/*- 2204076Spjd * Copyright (C) 1996 3219351Spjd * David L. Nugent. All rights reserved. 4204076Spjd * 5204076Spjd * Redistribution and use in source and binary forms, with or without 6204076Spjd * modification, are permitted provided that the following conditions 7204076Spjd * are met: 8204076Spjd * 1. Redistributions of source code must retain the above copyright 9204076Spjd * notice, this list of conditions and the following disclaimer. 10204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 11204076Spjd * notice, this list of conditions and the following disclaimer in the 12204076Spjd * documentation and/or other materials provided with the distribution. 13204076Spjd * 14204076Spjd * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204076Spjd * SUCH DAMAGE. 25204076Spjd */ 26204076Spjd 27204076Spjd#ifndef lint 28204076Spjdstatic const char rcsid[] = 29204076Spjd "$FreeBSD: head/usr.sbin/pw/pw_conf.c 70133 2000-12-18 01:35:56Z dougb $"; 30204076Spjd#endif /* not lint */ 31204076Spjd 32204076Spjd#include <string.h> 33204076Spjd#include <ctype.h> 34204076Spjd#include <fcntl.h> 35204076Spjd 36204076Spjd#include "pw.h" 37204076Spjd 38204076Spjd#define debugging 0 39204076Spjd 40204076Spjdenum { 41204076Spjd _UC_NONE, 42204076Spjd _UC_DEFAULTPWD, 43204076Spjd _UC_REUSEUID, 44204076Spjd _UC_REUSEGID, 45204076Spjd _UC_NISPASSWD, 46204076Spjd _UC_DOTDIR, 47204076Spjd _UC_NEWMAIL, 48211982Spjd _UC_LOGFILE, 49204076Spjd _UC_HOMEROOT, 50204076Spjd _UC_SHELLPATH, 51204076Spjd _UC_SHELLS, 52204076Spjd _UC_DEFAULTSHELL, 53204076Spjd _UC_DEFAULTGROUP, 54204076Spjd _UC_EXTRAGROUPS, 55204076Spjd _UC_DEFAULTCLASS, 56204076Spjd _UC_MINUID, 57204076Spjd _UC_MAXUID, 58204076Spjd _UC_MINGID, 59204076Spjd _UC_MAXGID, 60212038Spjd _UC_EXPIRE, 61204076Spjd _UC_PASSWORD, 62204076Spjd _UC_FIELDS 63204076Spjd}; 64211886Spjd 65204076Spjdstatic char bourne_shell[] = "sh"; 66204076Spjd 67204076Spjdstatic char *system_shells[_UC_MAXSHELLS] = 68204076Spjd{ 69204076Spjd bourne_shell, 70204076Spjd "csh", 71210886Spjd "tcsh" 72210886Spjd}; 73210886Spjd 74204076Spjdstatic char const *booltrue[] = 75204076Spjd{ 76204076Spjd "yes", "true", "1", "on", NULL 77204076Spjd}; 78204076Spjdstatic char const *boolfalse[] = 79204076Spjd{ 80204076Spjd "no", "false", "0", "off", NULL 81204076Spjd}; 82204076Spjd 83204076Spjdstatic struct userconf config = 84204076Spjd{ 85204076Spjd 0, /* Default password for new users? (nologin) */ 86204076Spjd 0, /* Reuse uids? */ 87204076Spjd 0, /* Reuse gids? */ 88204076Spjd NULL, /* NIS version of the passwd file */ 89219818Spjd "/usr/share/skel", /* Where to obtain skeleton files */ 90204076Spjd NULL, /* Mail to send to new accounts */ 91204076Spjd "/var/log/userlog", /* Where to log changes */ 92226859Spjd "/home", /* Where to create home directory */ 93226859Spjd "/bin", /* Where shells are located */ 94226859Spjd system_shells, /* List of shells (first is default) */ 95226859Spjd bourne_shell, /* Default shell */ 96226859Spjd NULL, /* Default group name */ 97226859Spjd NULL, /* Default (additional) groups */ 98226859Spjd NULL, /* Default login class */ 99226859Spjd 1000, 32000, /* Allowed range of uids */ 100226859Spjd 1000, 32000, /* Allowed range of gids */ 101204076Spjd 0, /* Days until account expires */ 102204076Spjd 0, /* Days until password expires */ 103204076Spjd 0 /* size of default_group array */ 104204076Spjd}; 105204076Spjd 106204076Spjdstatic char const *comments[_UC_FIELDS] = 107204076Spjd{ 108204076Spjd "#\n# pw.conf - user/group configuration defaults\n#\n", 109204076Spjd "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 110204076Spjd "\n# Reuse gaps in uid sequence? (yes or no)\n", 111204076Spjd "\n# Reuse gaps in gid sequence? (yes or no)\n", 112204076Spjd "\n# Path to the NIS passwd file (blank or 'no' for none)\n", 113204076Spjd "\n# Obtain default dotfiles from this directory\n", 114204076Spjd "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 115204076Spjd "\n# Log add/change/remove information in this file\n", 116204076Spjd "\n# Root directory in which $HOME directory is created\n", 117204076Spjd "\n# Colon separated list of directories containing valid shells\n", 118204076Spjd "\n# Comma separated list of available shells (without paths)\n", 119204076Spjd "\n# Default shell (without path)\n", 120204076Spjd "\n# Default group (leave blank for new group per user)\n", 121204076Spjd "\n# Extra groups for new users\n", 122204076Spjd "\n# Default login class for new users\n", 123204076Spjd "\n# Range of valid default user ids\n", 124204076Spjd NULL, 125204076Spjd "\n# Range of valid default group ids\n", 126204076Spjd NULL, 127204076Spjd "\n# Days after which account expires (0=disabled)\n", 128204076Spjd "\n# Days after which password expires (0=disabled)\n" 129204076Spjd}; 130204076Spjd 131204076Spjdstatic char const *kwds[] = 132204076Spjd{ 133204076Spjd "", 134204076Spjd "defaultpasswd", 135204076Spjd "reuseuids", 136204076Spjd "reusegids", 137204076Spjd "nispasswd", 138204076Spjd "skeleton", 139204076Spjd "newmail", 140204076Spjd "logfile", 141204076Spjd "home", 142204076Spjd "shellpath", 143204076Spjd "shells", 144204076Spjd "defaultshell", 145204076Spjd "defaultgroup", 146204076Spjd "extragroups", 147204076Spjd "defaultclass", 148204076Spjd "minuid", 149204076Spjd "maxuid", 150204076Spjd "mingid", 151204076Spjd "maxgid", 152204076Spjd "expire_days", 153204076Spjd "password_days", 154204076Spjd NULL 155204076Spjd}; 156204076Spjd 157204076Spjdstatic char * 158204076Spjdunquote(char const * str) 159204076Spjd{ 160204076Spjd if (str && (*str == '"' || *str == '\'')) { 161204076Spjd char *p = strchr(str + 1, *str); 162204076Spjd 163204076Spjd if (p != NULL) 164204076Spjd *p = '\0'; 165204076Spjd return (char *) (*++str ? str : NULL); 166204076Spjd } 167204076Spjd return (char *) str; 168204076Spjd} 169204076Spjd 170204076Spjdint 171204076Spjdboolean_val(char const * str, int dflt) 172204076Spjd{ 173204076Spjd if ((str = unquote(str)) != NULL) { 174204076Spjd int i; 175204076Spjd 176204076Spjd for (i = 0; booltrue[i]; i++) 177204076Spjd if (strcmp(str, booltrue[i]) == 0) 178204076Spjd return 1; 179204076Spjd for (i = 0; boolfalse[i]; i++) 180204076Spjd if (strcmp(str, boolfalse[i]) == 0) 181204076Spjd return 0; 182204076Spjd 183204076Spjd /* 184204076Spjd * Special cases for defaultpassword 185204076Spjd */ 186204076Spjd if (strcmp(str, "random") == 0) 187214692Spjd return -1; 188214692Spjd if (strcmp(str, "none") == 0) 189214692Spjd return -2; 190204076Spjd } 191214692Spjd return dflt; 192214692Spjd} 193214692Spjd 194214692Spjdchar const * 195219864Spjdboolean_str(int val) 196214692Spjd{ 197204076Spjd if (val == -1) 198214692Spjd return "random"; 199214692Spjd else if (val == -2) 200214692Spjd return "none"; 201214692Spjd else 202204076Spjd return val ? booltrue[0] : boolfalse[0]; 203204076Spjd} 204204076Spjd 205204076Spjdchar * 206204076Spjdnewstr(char const * p) 207204076Spjd{ 208204076Spjd char *q = NULL; 209204076Spjd 210204076Spjd if ((p = unquote(p)) != NULL) { 211204076Spjd int l = strlen(p) + 1; 212204076Spjd 213204076Spjd if ((q = malloc(l)) != NULL) 214209183Spjd memcpy(q, p, l); 215209183Spjd } 216209183Spjd return q; 217209183Spjd} 218204076Spjd 219204076Spjd#define LNBUFSZ 1024 220204076Spjd 221204076Spjd 222204076Spjdstruct userconf * 223204076Spjdread_userconfig(char const * file) 224204076Spjd{ 225204076Spjd FILE *fp; 226204076Spjd 227204076Spjd extendarray(&config.groups, &config.numgroups, 200); 228204076Spjd memset(config.groups, 0, config.numgroups * sizeof(char *)); 229204076Spjd if (file == NULL) 230204076Spjd file = _PATH_PW_CONF; 231220898Spjd if ((fp = fopen(file, "r")) != NULL) { 232204076Spjd int buflen = LNBUFSZ; 233204076Spjd char *buf = malloc(buflen); 234204076Spjd 235204076Spjd nextline: 236204076Spjd while (fgets(buf, buflen, fp) != NULL) { 237204076Spjd char *p; 238204076Spjd 239204076Spjd while ((p = strchr(buf, '\n')) == NULL) { 240204076Spjd int l; 241211982Spjd if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) { 242204076Spjd int ch; 243204076Spjd while ((ch = fgetc(fp)) != '\n' && ch != EOF); 244204076Spjd goto nextline; /* Ignore it */ 245204076Spjd } 246204076Spjd l = strlen(buf); 247204076Spjd if (fgets(buf + l, buflen - l, fp) == NULL) 248204076Spjd break; /* Unterminated last line */ 249204076Spjd } 250204076Spjd 251204076Spjd if (p != NULL) 252204076Spjd *p = '\0'; 253213533Spjd 254204076Spjd if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 255204076Spjd static char const toks[] = " \t\r\n,="; 256204076Spjd char *q = strtok(NULL, toks); 257229945Spjd int i = 0; 258213531Spjd 259213531Spjd while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 260204076Spjd ++i; 261204076Spjd#if debugging 262204076Spjd if (i == _UC_FIELDS) 263204076Spjd printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 264204076Spjd else 265204076Spjd printf("Got kwd[%s]=%s\n", p, q); 266204076Spjd#endif 267204076Spjd switch (i) { 268204076Spjd case _UC_DEFAULTPWD: 269212899Spjd config.default_password = boolean_val(q, 1); 270204076Spjd break; 271204076Spjd case _UC_REUSEUID: 272204076Spjd config.reuse_uids = boolean_val(q, 0); 273204076Spjd break; 274218138Spjd case _UC_REUSEGID: 275204076Spjd config.reuse_gids = boolean_val(q, 0); 276204076Spjd break; 277204076Spjd case _UC_NISPASSWD: 278204076Spjd config.nispasswd = (q == NULL || !boolean_val(q, 1)) 279204076Spjd ? NULL : newstr(q); 280204076Spjd break; 281204076Spjd case _UC_DOTDIR: 282212899Spjd config.dotdir = (q == NULL || !boolean_val(q, 1)) 283204076Spjd ? NULL : newstr(q); 284204076Spjd break; 285204076Spjd case _UC_NEWMAIL: 286204076Spjd config.newmail = (q == NULL || !boolean_val(q, 1)) 287204076Spjd ? NULL : newstr(q); 288204076Spjd break; 289204076Spjd case _UC_LOGFILE: 290204076Spjd config.logfile = (q == NULL || !boolean_val(q, 1)) 291204076Spjd ? NULL : newstr(q); 292204076Spjd break; 293204076Spjd case _UC_HOMEROOT: 294204076Spjd config.home = (q == NULL || !boolean_val(q, 1)) 295204076Spjd ? "/home" : newstr(q); 296204076Spjd break; 297204076Spjd case _UC_SHELLPATH: 298204076Spjd config.shelldir = (q == NULL || !boolean_val(q, 1)) 299204076Spjd ? "/bin" : newstr(q); 300204076Spjd break; 301218138Spjd case _UC_SHELLS: 302218138Spjd for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 303204076Spjd system_shells[i] = newstr(q); 304204076Spjd if (i > 0) 305225786Spjd while (i < _UC_MAXSHELLS) 306204076Spjd system_shells[i++] = NULL; 307204076Spjd break; 308225830Spjd case _UC_DEFAULTSHELL: 309225830Spjd config.shell_default = (q == NULL || !boolean_val(q, 1)) 310225830Spjd ? (char *) bourne_shell : newstr(q); 311225830Spjd break; 312225830Spjd case _UC_DEFAULTGROUP: 313225830Spjd q = unquote(q); 314225830Spjd config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) 315225830Spjd ? NULL : newstr(q); 316225830Spjd break; 317225830Spjd case _UC_EXTRAGROUPS: 318225830Spjd for (i = 0; q != NULL; q = strtok(NULL, toks)) { 319204076Spjd if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) 320204076Spjd config.groups[i++] = newstr(q); 321204076Spjd } 322210881Spjd if (i > 0) 323210881Spjd while (i < config.numgroups) 324210881Spjd config.groups[i++] = NULL; 325210881Spjd break; 326210881Spjd case _UC_DEFAULTCLASS: 327210881Spjd config.default_class = (q == NULL || !boolean_val(q, 1)) 328210881Spjd ? NULL : newstr(q); 329204076Spjd break; 330204076Spjd case _UC_MINUID: 331204076Spjd if ((q = unquote(q)) != NULL && isdigit(*q)) 332204076Spjd config.min_uid = (uid_t) atol(q); 333204076Spjd break; 334204076Spjd case _UC_MAXUID: 335204076Spjd if ((q = unquote(q)) != NULL && isdigit(*q)) 336204076Spjd config.max_uid = (uid_t) atol(q); 337204076Spjd break; 338204076Spjd case _UC_MINGID: 339204076Spjd if ((q = unquote(q)) != NULL && isdigit(*q)) 340204076Spjd config.min_gid = (gid_t) atol(q); 341204076Spjd break; 342204076Spjd case _UC_MAXGID: 343204076Spjd if ((q = unquote(q)) != NULL && isdigit(*q)) 344204076Spjd config.max_gid = (gid_t) atol(q); 345204076Spjd break; 346204076Spjd case _UC_EXPIRE: 347204076Spjd if ((q = unquote(q)) != NULL && isdigit(*q)) 348204076Spjd config.expire_days = atoi(q); 349204076Spjd break; 350204076Spjd case _UC_PASSWORD: 351204076Spjd if ((q = unquote(q)) != NULL && isdigit(*q)) 352204076Spjd config.password_days = atoi(q); 353204076Spjd break; 354204076Spjd case _UC_FIELDS: 355204076Spjd case _UC_NONE: 356204076Spjd break; 357204076Spjd } 358204076Spjd } 359204076Spjd } 360204076Spjd free(buf); 361204076Spjd fclose(fp); 362204076Spjd } 363204076Spjd return &config; 364204076Spjd} 365204076Spjd 366204076Spjd 367204076Spjdint 368204076Spjdwrite_userconfig(char const * file) 369204076Spjd{ 370204076Spjd int fd; 371204076Spjd 372204076Spjd if (file == NULL) 373204076Spjd file = _PATH_PW_CONF; 374204076Spjd 375204076Spjd if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { 376204076Spjd FILE *fp; 377204076Spjd 378204076Spjd if ((fp = fdopen(fd, "w")) == NULL) 379204076Spjd close(fd); 380204076Spjd else { 381204076Spjd int i, j, k; 382204076Spjd int len = LNBUFSZ; 383204076Spjd char *buf = malloc(len); 384204076Spjd 385204076Spjd for (i = _UC_NONE; i < _UC_FIELDS; i++) { 386204076Spjd int quote = 1; 387204076Spjd char const *val = buf; 388204076Spjd 389204076Spjd *buf = '\0'; 390204076Spjd switch (i) { 391204076Spjd case _UC_DEFAULTPWD: 392204076Spjd val = boolean_str(config.default_password); 393204076Spjd break; 394204076Spjd case _UC_REUSEUID: 395204076Spjd val = boolean_str(config.reuse_uids); 396204076Spjd break; 397204076Spjd case _UC_REUSEGID: 398204076Spjd val = boolean_str(config.reuse_gids); 399204076Spjd break; 400204076Spjd case _UC_NISPASSWD: 401204076Spjd val = config.nispasswd ? config.nispasswd : ""; 402204076Spjd quote = 0; 403204076Spjd break; 404204076Spjd case _UC_DOTDIR: 405204076Spjd val = config.dotdir ? config.dotdir : boolean_str(0); 406204076Spjd break; 407204076Spjd case _UC_NEWMAIL: 408204076Spjd val = config.newmail ? config.newmail : boolean_str(0); 409204076Spjd break; 410204076Spjd case _UC_LOGFILE: 411204076Spjd val = config.logfile ? config.logfile : boolean_str(0); 412204076Spjd break; 413204076Spjd case _UC_HOMEROOT: 414204076Spjd val = config.home; 415204076Spjd break; 416204076Spjd case _UC_SHELLPATH: 417204076Spjd val = config.shelldir; 418204076Spjd break; 419204076Spjd case _UC_SHELLS: 420204076Spjd for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) { 421204076Spjd char lbuf[64]; 422204076Spjd int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]); 423204076Spjd if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) { 424204076Spjd strcpy(buf + k, lbuf); 425204076Spjd k += l; 426204076Spjd } 427204076Spjd } 428204076Spjd quote = 0; 429204076Spjd break; 430204076Spjd case _UC_DEFAULTSHELL: 431204076Spjd val = config.shell_default ? config.shell_default : bourne_shell; 432204076Spjd break; 433204076Spjd case _UC_DEFAULTGROUP: 434204076Spjd val = config.default_group ? config.default_group : ""; 435204076Spjd break; 436204076Spjd case _UC_EXTRAGROUPS: 437204076Spjd extendarray(&config.groups, &config.numgroups, 200); 438204076Spjd for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) { 439204076Spjd char lbuf[64]; 440204076Spjd int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]); 441204076Spjd if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) { 442214284Spjd strcpy(buf + k, lbuf); 443214284Spjd k += l; 444214284Spjd } 445214284Spjd } 446214284Spjd quote = 0; 447214284Spjd break; 448214284Spjd case _UC_DEFAULTCLASS: 449214284Spjd val = config.default_class ? config.default_class : ""; 450214284Spjd break; 451214284Spjd case _UC_MINUID: 452214284Spjd sprintf(buf, "%lu", (unsigned long) config.min_uid); 453214284Spjd quote = 0; 454229945Spjd break; 455214284Spjd case _UC_MAXUID: 456214284Spjd sprintf(buf, "%lu", (unsigned long) config.max_uid); 457214284Spjd quote = 0; 458214284Spjd break; 459214284Spjd case _UC_MINGID: 460204076Spjd sprintf(buf, "%lu", (unsigned long) config.min_gid); 461204076Spjd quote = 0; 462204076Spjd break; 463204076Spjd case _UC_MAXGID: 464204076Spjd sprintf(buf, "%lu", (unsigned long) config.max_gid); 465204076Spjd quote = 0; 466229945Spjd break; 467204076Spjd case _UC_EXPIRE: 468204076Spjd sprintf(buf, "%d", config.expire_days); 469204076Spjd quote = 0; 470229945Spjd break; 471204076Spjd case _UC_PASSWORD: 472204076Spjd sprintf(buf, "%d", config.password_days); 473204076Spjd quote = 0; 474204076Spjd break; 475229945Spjd case _UC_NONE: 476204076Spjd break; 477204076Spjd } 478229945Spjd 479204076Spjd if (comments[i]) 480204076Spjd fputs(comments[i], fp); 481204076Spjd 482204076Spjd if (*kwds[i]) { 483204076Spjd if (quote) 484204076Spjd fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 485204076Spjd else 486204076Spjd fprintf(fp, "%s = %s\n", kwds[i], val); 487204076Spjd#if debugging 488204076Spjd printf("WROTE: %s = %s\n", kwds[i], val); 489204076Spjd#endif 490204076Spjd } 491209181Spjd } 492204076Spjd free(buf); 493204076Spjd return fclose(fp) != EOF; 494204076Spjd } 495214284Spjd } 496214284Spjd return 0; 497214284Spjd} 498214284Spjd