pw_conf.c revision 20302
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 * $Id: pw_conf.c,v 1.1.1.1 1996/12/09 14:05:35 joerg Exp $ 27 */ 28 29#include <string.h> 30#include <ctype.h> 31#include <fcntl.h> 32 33#include "pw.h" 34 35#define debugging 0 36 37enum { 38 _UC_NONE, 39 _UC_DEFAULTPWD, 40 _UC_REUSEUID, 41 _UC_REUSEGID, 42 _UC_DOTDIR, 43 _UC_NEWMAIL, 44 _UC_LOGFILE, 45 _UC_HOMEROOT, 46 _UC_SHELLPATH, 47 _UC_SHELLS, 48 _UC_DEFAULTSHELL, 49 _UC_DEFAULTGROUP, 50 _UC_EXTRAGROUPS, 51 _UC_DEFAULTCLASS, 52 _UC_MINUID, 53 _UC_MAXUID, 54 _UC_MINGID, 55 _UC_MAXGID, 56 _UC_EXPIRE, 57 _UC_PASSWORD, 58 _UC_FIELDS 59}; 60 61static char bourne_shell[] = "sh"; 62 63static char *system_shells[_UC_MAXSHELLS] = 64{ 65 bourne_shell, 66 "csh" 67}; 68 69static char *default_groups[_UC_MAXGROUPS] = 70{ 71 NULL 72}; 73 74static char const *booltrue[] = 75{ 76 "yes", "true", "1", "on", NULL 77}; 78static char const *boolfalse[] = 79{ 80 "no", "false", "0", "off", NULL 81}; 82 83static struct userconf config = 84{ 85 0, /* Default password for new users? (nologin) */ 86 0, /* Reuse uids? */ 87 0, /* Reuse gids? */ 88 "/usr/share/skel", /* Where to obtain skeleton files */ 89 NULL, /* Mail to send to new accounts */ 90 "/var/log/userlog", /* Where to log changes */ 91 "/home", /* Where to create home directory */ 92 "/bin", /* Where shells are located */ 93 system_shells, /* List of shells (first is default) */ 94 bourne_shell, /* Default shell */ 95 NULL, /* Default group name */ 96 default_groups, /* Default (additional) groups */ 97 NULL, /* Default login class */ 98 1000, 32000, /* Allowed range of uids */ 99 1000, 32000, /* Allowed range of gids */ 100 0, /* Days until account expires */ 101 0 /* Days until password expires */ 102}; 103 104static char const *comments[_UC_FIELDS] = 105{ 106 "#\n# pw.conf - user/group configuration defaults\n#\n", 107 "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n", 108 "\n# Reuse gaps in uid sequence? (yes or no)\n", 109 "\n# Reuse gaps in gid sequence? (yes or no)\n", 110 "\n# Obtain default dotfiles from this directory\n", 111 "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 112 "\n# Log add/change/remove information in this file\n", 113 "\n# Root directory in which $HOME directory is created\n", 114 "\n# Colon separated list of directories containing valid shells\n", 115 "\n# Space separated list of available shells (without paths)\n", 116 "\n# Default shell (without path)\n", 117 "\n# Default group (leave blank for new group per user)\n", 118 "\n# Extra groups for new users\n", 119 "\n# Default login class for new users\n", 120 "\n# Range of valid default user ids\n", 121 NULL, 122 "\n# Range of valid default group ids\n", 123 NULL, 124 "\n# Days after which account expires (0=disabled)\n", 125 "\n# Days after which password expires (0=disabled)\n" 126}; 127 128static char const *kwds[] = 129{ 130 "", 131 "defaultpasswd", 132 "reuseuids", 133 "reusegids", 134 "skeleton", 135 "newmail", 136 "logfile", 137 "home", 138 "shellpath", 139 "shells", 140 "defaultshell", 141 "defaultgroup", 142 "extragroups", 143 "defaultclass", 144 "minuid", 145 "maxuid", 146 "mingid", 147 "maxgid", 148 "expire_days", 149 "password_days", 150 NULL 151}; 152 153static char * 154unquote(char const * str) 155{ 156 if (str && (*str == '"' || *str == '\'')) { 157 char *p = strchr(str + 1, *str); 158 159 if (p != NULL) 160 *p = '\0'; 161 return (char *) (*++str ? str : NULL); 162 } 163 return (char *) str; 164} 165 166int 167boolean_val(char const * str, int dflt) 168{ 169 if ((str = unquote(str)) != NULL) { 170 int i; 171 172 for (i = 0; booltrue[i]; i++) 173 if (strcmp(str, booltrue[i]) == 0) 174 return 1; 175 for (i = 0; boolfalse[i]; i++) 176 if (strcmp(str, boolfalse[i]) == 0) 177 return 0; 178 179 /* 180 * Special cases for defaultpassword 181 */ 182 if (strcmp(str, "random") == 0) 183 return -1; 184 if (strcmp(str, "none") == 0) 185 return -2; 186 } 187 return dflt; 188} 189 190char const * 191boolean_str(int val) 192{ 193 if (val == -1) 194 return "random"; 195 else if (val == -2) 196 return "none"; 197 else 198 return val ? booltrue[0] : boolfalse[0]; 199} 200 201char * 202newstr(char const * p) 203{ 204 char *q = NULL; 205 206 if ((p = unquote(p)) != NULL) { 207 int l = strlen(p) + 1; 208 209 if ((q = malloc(l)) != NULL) 210 memcpy(q, p, l); 211 } 212 return q; 213} 214 215 216struct userconf * 217read_userconfig(char const * file) 218{ 219 FILE *fp; 220 221 if (file == NULL) 222 file = _PATH_PW_CONF; 223 if ((fp = fopen(file, "r")) != NULL) { 224 char buf[_UC_MAXLINE]; 225 226 while (fgets(buf, sizeof buf, fp) != NULL) { 227 char *p = strchr(buf, '\n'); 228 229 if (p == NULL) { /* Line too long */ 230 int ch; 231 232 while ((ch = fgetc(fp)) != '\n' && ch != EOF); 233 } else { 234 *p = '\0'; 235 if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 236 static char const toks[] = " \t\r\n,="; 237 char *q = strtok(NULL, toks); 238 int i = 0; 239 240 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 241 ++i; 242#if debugging 243 if (i == _UC_FIELDS) 244 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 245 else 246 printf("Got kwd[%s]=%s\n", p, q); 247#endif 248 switch (i) { 249 case _UC_DEFAULTPWD: 250 config.default_password = boolean_val(q, 1); 251 break; 252 case _UC_REUSEUID: 253 config.reuse_uids = boolean_val(q, 0); 254 break; 255 case _UC_REUSEGID: 256 config.reuse_gids = boolean_val(q, 0); 257 break; 258 case _UC_DOTDIR: 259 config.dotdir = (q == NULL || !boolean_val(q, 1)) 260 ? NULL : newstr(q); 261 break; 262 case _UC_NEWMAIL: 263 config.newmail = (q == NULL || !boolean_val(q, 1)) 264 ? NULL : newstr(q); 265 break; 266 case _UC_LOGFILE: 267 config.logfile = (q == NULL || !boolean_val(q, 1)) 268 ? NULL : newstr(q); 269 break; 270 case _UC_HOMEROOT: 271 config.home = (q == NULL || !boolean_val(q, 1)) 272 ? "/home" : newstr(q); 273 break; 274 case _UC_SHELLPATH: 275 config.shelldir = (q == NULL || !boolean_val(q, 1)) 276 ? "/bin" : newstr(q); 277 break; 278 case _UC_SHELLS: 279 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 280 system_shells[i] = newstr(q); 281 if (i > 0) 282 while (i < _UC_MAXSHELLS) 283 system_shells[i++] = NULL; 284 break; 285 case _UC_DEFAULTSHELL: 286 config.shell_default = (q == NULL || !boolean_val(q, 1)) 287 ? (char *) bourne_shell : newstr(q); 288 break; 289 case _UC_DEFAULTGROUP: 290 config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL) 291 ? NULL : newstr(q); 292 break; 293 case _UC_EXTRAGROUPS: 294 for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks)) 295 default_groups[i] = newstr(q); 296 if (i > 0) 297 while (i < _UC_MAXGROUPS) 298 default_groups[i++] = NULL; 299 break; 300 case _UC_DEFAULTCLASS: 301 config.default_class = (q == NULL || !boolean_val(q, 1)) 302 ? NULL : newstr(q); 303 break; 304 case _UC_MINUID: 305 if ((q = unquote(q)) != NULL && isdigit(*q)) 306 config.min_uid = (uid_t) atol(q); 307 break; 308 case _UC_MAXUID: 309 if ((q = unquote(q)) != NULL && isdigit(*q)) 310 config.max_uid = (uid_t) atol(q); 311 break; 312 case _UC_MINGID: 313 if ((q = unquote(q)) != NULL && isdigit(*q)) 314 config.min_gid = (gid_t) atol(q); 315 break; 316 case _UC_MAXGID: 317 if ((q = unquote(q)) != NULL && isdigit(*q)) 318 config.max_gid = (gid_t) atol(q); 319 break; 320 case _UC_EXPIRE: 321 if ((q = unquote(q)) != NULL && isdigit(*q)) 322 config.expire_days = atoi(q); 323 break; 324 case _UC_PASSWORD: 325 if ((q = unquote(q)) != NULL && isdigit(*q)) 326 config.password_days = atoi(q); 327 break; 328 case _UC_FIELDS: 329 case _UC_NONE: 330 break; 331 } 332 } 333 } 334 } 335 fclose(fp); 336 } 337 return &config; 338} 339 340 341int 342write_userconfig(char const * file) 343{ 344 int fd; 345 346 if (file == NULL) 347 file = _PATH_PW_CONF; 348 349 if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { 350 FILE *fp; 351 352 if ((fp = fdopen(fd, "w")) == NULL) 353 close(fd); 354 else { 355 int i, j, k; 356 char buf[_UC_MAXLINE]; 357 358 for (i = _UC_NONE; i < _UC_FIELDS; i++) { 359 int quote = 1; 360 char const *val = buf; 361 362 *buf = '\0'; 363 switch (i) { 364 case _UC_DEFAULTPWD: 365 val = boolean_str(config.default_password); 366 break; 367 case _UC_REUSEUID: 368 val = boolean_str(config.reuse_uids); 369 break; 370 case _UC_REUSEGID: 371 val = boolean_str(config.reuse_gids); 372 break; 373 case _UC_DOTDIR: 374 val = config.dotdir ? config.dotdir : boolean_str(0); 375 break; 376 case _UC_NEWMAIL: 377 val = config.newmail ? config.newmail : boolean_str(0); 378 break; 379 case _UC_LOGFILE: 380 val = config.logfile ? config.logfile : boolean_str(0); 381 break; 382 case _UC_HOMEROOT: 383 val = config.home; 384 break; 385 case _UC_SHELLPATH: 386 val = config.shelldir; 387 break; 388 case _UC_SHELLS: 389 for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) 390 k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]); 391 quote = 0; 392 break; 393 case _UC_DEFAULTSHELL: 394 val = config.shell_default ? config.shell_default : bourne_shell; 395 break; 396 case _UC_DEFAULTGROUP: 397 val = config.default_group ? config.default_group : ""; 398 break; 399 case _UC_EXTRAGROUPS: 400 for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++) 401 k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]); 402 quote = 0; 403 break; 404 case _UC_DEFAULTCLASS: 405 val = config.default_class ? config.default_class : ""; 406 break; 407 case _UC_MINUID: 408 sprintf(buf, "%lu", (unsigned long) config.min_uid); 409 quote = 0; 410 break; 411 case _UC_MAXUID: 412 sprintf(buf, "%lu", (unsigned long) config.max_uid); 413 quote = 0; 414 break; 415 case _UC_MINGID: 416 sprintf(buf, "%lu", (unsigned long) config.min_gid); 417 quote = 0; 418 break; 419 case _UC_MAXGID: 420 sprintf(buf, "%lu", (unsigned long) config.max_gid); 421 quote = 0; 422 break; 423 case _UC_EXPIRE: 424 sprintf(buf, "%d", config.expire_days); 425 quote = 0; 426 break; 427 case _UC_PASSWORD: 428 sprintf(buf, "%d", config.password_days); 429 quote = 0; 430 break; 431 case _UC_NONE: 432 break; 433 } 434 435 if (comments[i]) 436 fputs(comments[i], fp); 437 438 if (*kwds[i]) { 439 if (quote) 440 fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 441 else 442 fprintf(fp, "%s = %s\n", kwds[i], val); 443#if debugging 444 printf("WROTE: %s = %s\n", kwds[i], val); 445#endif 446 } 447 } 448 return fclose(fp) != EOF; 449 } 450 } 451 return 0; 452} 453