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