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