pw_conf.c revision 44229
1228753Smm/*- 2228753Smm * Copyright (C) 1996 3228753Smm * David L. Nugent. All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND 15228753Smm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16228753Smm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17228753Smm * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE 18228753Smm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19228753Smm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20228753Smm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21228753Smm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22228753Smm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23228753Smm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24228753Smm * SUCH DAMAGE. 25229592Smm */ 26228753Smm 27228753Smm#ifndef lint 28228753Smmstatic const char rcsid[] = 29228753Smm "$Id: pw_conf.c,v 1.7 1997/10/10 06:23:36 charnier Exp $"; 30228753Smm#endif /* not lint */ 31228753Smm 32228753Smm#include <string.h> 33228753Smm#include <ctype.h> 34228753Smm#include <fcntl.h> 35228753Smm 36228753Smm#include "pw.h" 37228753Smm 38228753Smm#define debugging 0 39228753Smm 40228753Smmenum { 41228753Smm _UC_NONE, 42228753Smm _UC_DEFAULTPWD, 43228753Smm _UC_REUSEUID, 44228753Smm _UC_REUSEGID, 45228753Smm _UC_NISPASSWD, 46228753Smm _UC_DOTDIR, 47228753Smm _UC_NEWMAIL, 48228753Smm _UC_LOGFILE, 49228753Smm _UC_HOMEROOT, 50228753Smm _UC_SHELLPATH, 51228753Smm _UC_SHELLS, 52228753Smm _UC_DEFAULTSHELL, 53228753Smm _UC_DEFAULTGROUP, 54228753Smm _UC_EXTRAGROUPS, 55228753Smm _UC_DEFAULTCLASS, 56228753Smm _UC_MINUID, 57228753Smm _UC_MAXUID, 58228753Smm _UC_MINGID, 59228753Smm _UC_MAXGID, 60228753Smm _UC_EXPIRE, 61228753Smm _UC_PASSWORD, 62228753Smm _UC_FIELDS 63228753Smm}; 64228753Smm 65228753Smmstatic char bourne_shell[] = "sh"; 66228753Smm 67228753Smmstatic char *system_shells[_UC_MAXSHELLS] = 68228753Smm{ 69228753Smm bourne_shell, 70228753Smm "csh" 71228753Smm}; 72228753Smm 73228753Smmstatic char const *booltrue[] = 74228753Smm{ 75228753Smm "yes", "true", "1", "on", NULL 76228753Smm}; 77228753Smmstatic char const *boolfalse[] = 78228753Smm{ 79228753Smm "no", "false", "0", "off", NULL 80228753Smm}; 81228753Smm 82228753Smmstatic struct userconf config = 83228753Smm{ 84228753Smm 0, /* Default password for new users? (nologin) */ 85228753Smm 0, /* Reuse uids? */ 86228753Smm 0, /* Reuse gids? */ 87228753Smm NULL, /* NIS version of the passwd file */ 88228753Smm "/usr/share/skel", /* Where to obtain skeleton files */ 89228753Smm NULL, /* Mail to send to new accounts */ 90228753Smm "/var/log/userlog", /* Where to log changes */ 91228753Smm "/home", /* Where to create home directory */ 92228753Smm "/bin", /* Where shells are located */ 93228753Smm system_shells, /* List of shells (first is default) */ 94228753Smm bourne_shell, /* Default shell */ 95228753Smm NULL, /* Default group name */ 96228753Smm NULL, /* Default (additional) groups */ 97228753Smm NULL, /* Default login class */ 98228753Smm 1000, 32000, /* Allowed range of uids */ 99228753Smm 1000, 32000, /* Allowed range of gids */ 100228753Smm 0, /* Days until account expires */ 101228753Smm 0 /* Days until password expires */ 102228753Smm}; 103228753Smm 104228753Smmstatic char const *comments[_UC_FIELDS] = 105228753Smm{ 106228753Smm "#\n# pw.conf - user/group configuration defaults\n#\n", 107228753Smm "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 108228753Smm "\n# Reuse gaps in uid sequence? (yes or no)\n", 109228753Smm "\n# Reuse gaps in gid sequence? (yes or no)\n", 110228753Smm "\n# Path to the NIS passwd file (blank or 'no' for none)\n", 111228753Smm "\n# Obtain default dotfiles from this directory\n", 112228753Smm "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 113228753Smm "\n# Log add/change/remove information in this file\n", 114228753Smm "\n# Root directory in which $HOME directory is created\n", 115228753Smm "\n# Colon separated list of directories containing valid shells\n", 116228753Smm "\n# Space separated list of available shells (without paths)\n", 117228753Smm "\n# Default shell (without path)\n", 118228753Smm "\n# Default group (leave blank for new group per user)\n", 119228753Smm "\n# Extra groups for new users\n", 120228753Smm "\n# Default login class for new users\n", 121228753Smm "\n# Range of valid default user ids\n", 122228753Smm NULL, 123228753Smm "\n# Range of valid default group ids\n", 124228753Smm NULL, 125228753Smm "\n# Days after which account expires (0=disabled)\n", 126228753Smm "\n# Days after which password expires (0=disabled)\n" 127228753Smm}; 128228753Smm 129228753Smmstatic char const *kwds[] = 130228753Smm{ 131228753Smm "", 132228753Smm "defaultpasswd", 133228753Smm "reuseuids", 134228753Smm "reusegids", 135228753Smm "nispasswd", 136228753Smm "skeleton", 137228753Smm "newmail", 138228753Smm "logfile", 139228753Smm "home", 140228753Smm "shellpath", 141228753Smm "shells", 142228753Smm "defaultshell", 143228753Smm "defaultgroup", 144228753Smm "extragroups", 145228753Smm "defaultclass", 146228753Smm "minuid", 147228753Smm "maxuid", 148228753Smm "mingid", 149228753Smm "maxgid", 150228753Smm "expire_days", 151228753Smm "password_days", 152228753Smm NULL 153228753Smm}; 154228753Smm 155228753Smmstatic char * 156228753Smmunquote(char const * str) 157228753Smm{ 158228753Smm if (str && (*str == '"' || *str == '\'')) { 159228753Smm char *p = strchr(str + 1, *str); 160228753Smm 161228753Smm if (p != NULL) 162228753Smm *p = '\0'; 163228753Smm return (char *) (*++str ? str : NULL); 164228753Smm } 165228753Smm return (char *) str; 166228753Smm} 167228753Smm 168228753Smmint 169228753Smmboolean_val(char const * str, int dflt) 170228753Smm{ 171228753Smm if ((str = unquote(str)) != NULL) { 172228753Smm int i; 173228753Smm 174228753Smm for (i = 0; booltrue[i]; i++) 175228753Smm if (strcmp(str, booltrue[i]) == 0) 176228753Smm return 1; 177228753Smm for (i = 0; boolfalse[i]; i++) 178228753Smm if (strcmp(str, boolfalse[i]) == 0) 179228753Smm return 0; 180228753Smm 181228753Smm /* 182228753Smm * Special cases for defaultpassword 183228753Smm */ 184228753Smm if (strcmp(str, "random") == 0) 185228753Smm return -1; 186228753Smm if (strcmp(str, "none") == 0) 187228753Smm return -2; 188228753Smm } 189228753Smm return dflt; 190228753Smm} 191228753Smm 192228753Smmchar const * 193228753Smmboolean_str(int val) 194228753Smm{ 195228753Smm if (val == -1) 196228753Smm return "random"; 197228753Smm else if (val == -2) 198228753Smm return "none"; 199228753Smm else 200228753Smm return val ? booltrue[0] : boolfalse[0]; 201228753Smm} 202228753Smm 203228753Smmchar * 204228753Smmnewstr(char const * p) 205228753Smm{ 206228753Smm char *q = NULL; 207228753Smm 208228753Smm if ((p = unquote(p)) != NULL) { 209228753Smm int l = strlen(p) + 1; 210228753Smm 211228753Smm if ((q = malloc(l)) != NULL) 212228753Smm memcpy(q, p, l); 213228753Smm } 214228753Smm return q; 215228753Smm} 216228753Smm 217228753Smm#define LNBUFSZ 1024 218228753Smm 219228753Smm 220228753Smmstruct userconf * 221228753Smmread_userconfig(char const * file) 222228753Smm{ 223228753Smm FILE *fp; 224228753Smm 225228753Smm extendarray(&config.groups, &config.numgroups, 200); 226228753Smm memset(config.groups, 0, config.numgroups * sizeof(char *)); 227228753Smm if (file == NULL) 228228753Smm file = _PATH_PW_CONF; 229228753Smm if ((fp = fopen(file, "r")) != NULL) { 230228753Smm int buflen = LNBUFSZ; 231228753Smm char *buf = malloc(buflen); 232228753Smm 233228753Smm nextline: 234228753Smm while (fgets(buf, buflen, fp) != NULL) { 235228753Smm char *p; 236228753Smm 237228753Smm while ((p = strchr(buf, '\n')) == NULL) { 238228753Smm int l; 239228753Smm if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) { 240228753Smm int ch; 241228753Smm while ((ch = fgetc(fp)) != '\n' && ch != EOF); 242228753Smm goto nextline; /* Ignore it */ 243228753Smm } 244228753Smm l = strlen(buf); 245228753Smm if (fgets(buf + l, buflen - l, fp) == NULL) 246228753Smm break; /* Unterminated last line */ 247228753Smm } 248228753Smm 249228753Smm if (p != NULL) 250228753Smm *p = '\0'; 251228753Smm 252228753Smm if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 253228753Smm static char const toks[] = " \t\r\n,="; 254228753Smm char *q = strtok(NULL, toks); 255228753Smm int i = 0; 256228753Smm 257228753Smm while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 258228753Smm ++i; 259228753Smm#if debugging 260228753Smm if (i == _UC_FIELDS) 261228753Smm printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 262228753Smm else 263228753Smm printf("Got kwd[%s]=%s\n", p, q); 264228753Smm#endif 265228753Smm switch (i) { 266228753Smm case _UC_DEFAULTPWD: 267228753Smm config.default_password = boolean_val(q, 1); 268228753Smm break; 269228753Smm case _UC_REUSEUID: 270228753Smm config.reuse_uids = boolean_val(q, 0); 271228753Smm break; 272228753Smm case _UC_REUSEGID: 273228753Smm config.reuse_gids = boolean_val(q, 0); 274228753Smm break; 275228753Smm case _UC_NISPASSWD: 276228753Smm config.nispasswd = (q == NULL || !boolean_val(q, 1)) 277228753Smm ? NULL : newstr(q); 278228753Smm break; 279228753Smm case _UC_DOTDIR: 280228753Smm config.dotdir = (q == NULL || !boolean_val(q, 1)) 281228753Smm ? NULL : newstr(q); 282228753Smm break; 283228753Smm case _UC_NEWMAIL: 284228753Smm config.newmail = (q == NULL || !boolean_val(q, 1)) 285228753Smm ? NULL : newstr(q); 286228753Smm break; 287228753Smm case _UC_LOGFILE: 288228753Smm config.logfile = (q == NULL || !boolean_val(q, 1)) 289228753Smm ? NULL : newstr(q); 290228753Smm break; 291228753Smm case _UC_HOMEROOT: 292228753Smm config.home = (q == NULL || !boolean_val(q, 1)) 293228753Smm ? "/home" : newstr(q); 294228753Smm break; 295228753Smm case _UC_SHELLPATH: 296228753Smm config.shelldir = (q == NULL || !boolean_val(q, 1)) 297228753Smm ? "/bin" : newstr(q); 298228753Smm break; 299228753Smm case _UC_SHELLS: 300228753Smm for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 301229592Smm system_shells[i] = newstr(q); 302229592Smm if (i > 0) 303229592Smm while (i < _UC_MAXSHELLS) 304229592Smm system_shells[i++] = NULL; 305229592Smm break; 306229592Smm case _UC_DEFAULTSHELL: 307229592Smm config.shell_default = (q == NULL || !boolean_val(q, 1)) 308229592Smm ? (char *) bourne_shell : newstr(q); 309229592Smm break; 310229592Smm case _UC_DEFAULTGROUP: 311229592Smm q = unquote(q); 312229592Smm config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) 313229592Smm ? NULL : newstr(q); 314229592Smm break; 315229592Smm case _UC_EXTRAGROUPS: 316229592Smm for (i = 0; q != NULL; q = strtok(NULL, toks)) { 317229592Smm if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) 318229592Smm config.groups[i++] = newstr(q); 319229592Smm } 320229592Smm if (i > 0) 321229592Smm while (i < config.numgroups) 322229592Smm config.groups[i++] = NULL; 323229592Smm break; 324229592Smm case _UC_DEFAULTCLASS: 325229592Smm config.default_class = (q == NULL || !boolean_val(q, 1)) 326 ? NULL : newstr(q); 327 break; 328 case _UC_MINUID: 329 if ((q = unquote(q)) != NULL && isdigit(*q)) 330 config.min_uid = (uid_t) atol(q); 331 break; 332 case _UC_MAXUID: 333 if ((q = unquote(q)) != NULL && isdigit(*q)) 334 config.max_uid = (uid_t) atol(q); 335 break; 336 case _UC_MINGID: 337 if ((q = unquote(q)) != NULL && isdigit(*q)) 338 config.min_gid = (gid_t) atol(q); 339 break; 340 case _UC_MAXGID: 341 if ((q = unquote(q)) != NULL && isdigit(*q)) 342 config.max_gid = (gid_t) atol(q); 343 break; 344 case _UC_EXPIRE: 345 if ((q = unquote(q)) != NULL && isdigit(*q)) 346 config.expire_days = atoi(q); 347 break; 348 case _UC_PASSWORD: 349 if ((q = unquote(q)) != NULL && isdigit(*q)) 350 config.password_days = atoi(q); 351 break; 352 case _UC_FIELDS: 353 case _UC_NONE: 354 break; 355 } 356 } 357 } 358 free(buf); 359 fclose(fp); 360 } 361 return &config; 362} 363 364 365int 366write_userconfig(char const * file) 367{ 368 int fd; 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 FILE *fp; 375 376 if ((fp = fdopen(fd, "w")) == NULL) 377 close(fd); 378 else { 379 int i, j, k; 380 int len = LNBUFSZ; 381 char *buf = malloc(len); 382 383 for (i = _UC_NONE; i < _UC_FIELDS; i++) { 384 int quote = 1; 385 char const *val = buf; 386 387 *buf = '\0'; 388 switch (i) { 389 case _UC_DEFAULTPWD: 390 val = boolean_str(config.default_password); 391 break; 392 case _UC_REUSEUID: 393 val = boolean_str(config.reuse_uids); 394 break; 395 case _UC_REUSEGID: 396 val = boolean_str(config.reuse_gids); 397 break; 398 case _UC_NISPASSWD: 399 val = config.nispasswd ? config.nispasswd : ""; 400 quote = 0; 401 break; 402 case _UC_DOTDIR: 403 val = config.dotdir ? config.dotdir : boolean_str(0); 404 break; 405 case _UC_NEWMAIL: 406 val = config.newmail ? config.newmail : boolean_str(0); 407 break; 408 case _UC_LOGFILE: 409 val = config.logfile ? config.logfile : boolean_str(0); 410 break; 411 case _UC_HOMEROOT: 412 val = config.home; 413 break; 414 case _UC_SHELLPATH: 415 val = config.shelldir; 416 break; 417 case _UC_SHELLS: 418 for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) { 419 char lbuf[64]; 420 int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]); 421 if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) { 422 strcpy(buf + k, lbuf); 423 k += l; 424 } 425 } 426 quote = 0; 427 break; 428 case _UC_DEFAULTSHELL: 429 val = config.shell_default ? config.shell_default : bourne_shell; 430 break; 431 case _UC_DEFAULTGROUP: 432 val = config.default_group ? config.default_group : ""; 433 break; 434 case _UC_EXTRAGROUPS: 435 extendarray(&config.groups, &config.numgroups, 200); 436 for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) { 437 char lbuf[64]; 438 int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]); 439 if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) { 440 strcpy(buf + k, lbuf); 441 k += l; 442 } 443 } 444 quote = 0; 445 break; 446 case _UC_DEFAULTCLASS: 447 val = config.default_class ? config.default_class : ""; 448 break; 449 case _UC_MINUID: 450 sprintf(buf, "%lu", (unsigned long) config.min_uid); 451 quote = 0; 452 break; 453 case _UC_MAXUID: 454 sprintf(buf, "%lu", (unsigned long) config.max_uid); 455 quote = 0; 456 break; 457 case _UC_MINGID: 458 sprintf(buf, "%lu", (unsigned long) config.min_gid); 459 quote = 0; 460 break; 461 case _UC_MAXGID: 462 sprintf(buf, "%lu", (unsigned long) config.max_gid); 463 quote = 0; 464 break; 465 case _UC_EXPIRE: 466 sprintf(buf, "%d", config.expire_days); 467 quote = 0; 468 break; 469 case _UC_PASSWORD: 470 sprintf(buf, "%d", config.password_days); 471 quote = 0; 472 break; 473 case _UC_NONE: 474 break; 475 } 476 477 if (comments[i]) 478 fputs(comments[i], fp); 479 480 if (*kwds[i]) { 481 if (quote) 482 fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 483 else 484 fprintf(fp, "%s = %s\n", kwds[i], val); 485#if debugging 486 printf("WROTE: %s = %s\n", kwds[i], val); 487#endif 488 } 489 } 490 free(buf); 491 return fclose(fp) != EOF; 492 } 493 } 494 return 0; 495} 496