pw_conf.c revision 282718
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 282718 2015-05-10 10:02:09Z 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 size_t l; 217 218 if ((p = unquote(p)) == NULL) 219 return (NULL); 220 221 l = strlen(p) + 1; 222 223 if ((q = strndup(p, l)) == NULL) 224 err(1, "strndup()"); 225 226 return (q); 227} 228 229struct userconf * 230read_userconfig(char const * file) 231{ 232 FILE *fp; 233 char *buf, *p; 234 size_t linecap; 235 ssize_t linelen; 236 237 buf = NULL; 238 linecap = 0; 239 240 extendarray(&config.groups, &config.numgroups, 200); 241 memset(config.groups, 0, config.numgroups * sizeof(char *)); 242 if (file == NULL) 243 file = _PATH_PW_CONF; 244 245 if ((fp = fopen(file, "r")) != NULL) { 246 while ((linelen = getline(&buf, &linecap, fp)) > 0) { 247 if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 248 static char const toks[] = " \t\r\n,="; 249 char *q = strtok(NULL, toks); 250 int i = 0; 251 mode_t *modeset; 252 253 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 254 ++i; 255#if debugging 256 if (i == _UC_FIELDS) 257 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 258 else 259 printf("Got kwd[%s]=%s\n", p, q); 260#endif 261 switch (i) { 262 case _UC_DEFAULTPWD: 263 config.default_password = boolean_val(q, 1); 264 break; 265 case _UC_REUSEUID: 266 config.reuse_uids = boolean_val(q, 0); 267 break; 268 case _UC_REUSEGID: 269 config.reuse_gids = boolean_val(q, 0); 270 break; 271 case _UC_NISPASSWD: 272 config.nispasswd = (q == NULL || !boolean_val(q, 1)) 273 ? NULL : newstr(q); 274 break; 275 case _UC_DOTDIR: 276 config.dotdir = (q == NULL || !boolean_val(q, 1)) 277 ? NULL : newstr(q); 278 break; 279 case _UC_NEWMAIL: 280 config.newmail = (q == NULL || !boolean_val(q, 1)) 281 ? NULL : newstr(q); 282 break; 283 case _UC_LOGFILE: 284 config.logfile = (q == NULL || !boolean_val(q, 1)) 285 ? NULL : newstr(q); 286 break; 287 case _UC_HOMEROOT: 288 config.home = (q == NULL || !boolean_val(q, 1)) 289 ? "/home" : newstr(q); 290 break; 291 case _UC_HOMEMODE: 292 modeset = setmode(q); 293 config.homemode = (q == NULL || !boolean_val(q, 1)) 294 ? _DEF_DIRMODE : getmode(modeset, _DEF_DIRMODE); 295 free(modeset); 296 break; 297 case _UC_SHELLPATH: 298 config.shelldir = (q == NULL || !boolean_val(q, 1)) 299 ? "/bin" : newstr(q); 300 break; 301 case _UC_SHELLS: 302 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 303 system_shells[i] = newstr(q); 304 if (i > 0) 305 while (i < _UC_MAXSHELLS) 306 system_shells[i++] = NULL; 307 break; 308 case _UC_DEFAULTSHELL: 309 config.shell_default = (q == NULL || !boolean_val(q, 1)) 310 ? (char *) bourne_shell : newstr(q); 311 break; 312 case _UC_DEFAULTGROUP: 313 q = unquote(q); 314 config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL) 315 ? NULL : newstr(q); 316 break; 317 case _UC_EXTRAGROUPS: 318 for (i = 0; q != NULL; q = strtok(NULL, toks)) { 319 if (extendarray(&config.groups, &config.numgroups, i + 2) != -1) 320 config.groups[i++] = newstr(q); 321 } 322 if (i > 0) 323 while (i < config.numgroups) 324 config.groups[i++] = NULL; 325 break; 326 case _UC_DEFAULTCLASS: 327 config.default_class = (q == NULL || !boolean_val(q, 1)) 328 ? NULL : newstr(q); 329 break; 330 case _UC_MINUID: 331 if ((q = unquote(q)) != NULL && isdigit(*q)) 332 config.min_uid = (uid_t) atol(q); 333 break; 334 case _UC_MAXUID: 335 if ((q = unquote(q)) != NULL && isdigit(*q)) 336 config.max_uid = (uid_t) atol(q); 337 break; 338 case _UC_MINGID: 339 if ((q = unquote(q)) != NULL && isdigit(*q)) 340 config.min_gid = (gid_t) atol(q); 341 break; 342 case _UC_MAXGID: 343 if ((q = unquote(q)) != NULL && isdigit(*q)) 344 config.max_gid = (gid_t) atol(q); 345 break; 346 case _UC_EXPIRE: 347 if ((q = unquote(q)) != NULL && isdigit(*q)) 348 config.expire_days = atoi(q); 349 break; 350 case _UC_PASSWORD: 351 if ((q = unquote(q)) != NULL && isdigit(*q)) 352 config.password_days = atoi(q); 353 break; 354 case _UC_FIELDS: 355 case _UC_NONE: 356 break; 357 } 358 } 359 } 360 if (linecap > 0) 361 free(buf); 362 fclose(fp); 363 } 364 return &config; 365} 366 367 368int 369write_userconfig(char const * file) 370{ 371 int fd; 372 int i, j; 373 struct sbuf *buf; 374 FILE *fp; 375 376 if (file == NULL) 377 file = _PATH_PW_CONF; 378 379 if ((fd = open(file, O_CREAT|O_RDWR|O_TRUNC|O_EXLOCK, 0644)) == -1) 380 return (0); 381 382 if ((fp = fdopen(fd, "w")) == NULL) { 383 close(fd); 384 return (0); 385 } 386 387 buf = sbuf_new_auto(); 388 for (i = _UC_NONE; i < _UC_FIELDS; i++) { 389 int quote = 1; 390 391 sbuf_clear(buf); 392 switch (i) { 393 case _UC_DEFAULTPWD: 394 sbuf_cat(buf, boolean_str(config.default_password)); 395 break; 396 case _UC_REUSEUID: 397 sbuf_cat(buf, boolean_str(config.reuse_uids)); 398 break; 399 case _UC_REUSEGID: 400 sbuf_cat(buf, boolean_str(config.reuse_gids)); 401 break; 402 case _UC_NISPASSWD: 403 sbuf_cat(buf, config.nispasswd ? config.nispasswd : 404 ""); 405 quote = 0; 406 break; 407 case _UC_DOTDIR: 408 sbuf_cat(buf, config.dotdir ? config.dotdir : 409 boolean_str(0)); 410 break; 411 case _UC_NEWMAIL: 412 sbuf_cat(buf, config.newmail ? config.newmail : 413 boolean_str(0)); 414 break; 415 case _UC_LOGFILE: 416 sbuf_cat(buf, config.logfile ? config.logfile : 417 boolean_str(0)); 418 break; 419 case _UC_HOMEROOT: 420 sbuf_cat(buf, config.home); 421 break; 422 case _UC_HOMEMODE: 423 sbuf_printf(buf, "%04o", config.homemode); 424 quote = 0; 425 break; 426 case _UC_SHELLPATH: 427 sbuf_cat(buf, config.shelldir); 428 break; 429 case _UC_SHELLS: 430 for (j = 0; j < _UC_MAXSHELLS && 431 system_shells[j] != NULL; j++) 432 sbuf_printf(buf, "%s\"%s\"", j ? 433 "," : "", system_shells[j]); 434 quote = 0; 435 break; 436 case _UC_DEFAULTSHELL: 437 sbuf_cat(buf, config.shell_default ? 438 config.shell_default : bourne_shell); 439 break; 440 case _UC_DEFAULTGROUP: 441 sbuf_cat(buf, config.default_group ? 442 config.default_group : ""); 443 break; 444 case _UC_EXTRAGROUPS: 445 for (j = 0; j < config.numgroups && 446 config.groups[j] != NULL; j++) 447 sbuf_printf(buf, "%s\"%s\"", j ? 448 "," : "", config.groups[j]); 449 quote = 0; 450 break; 451 case _UC_DEFAULTCLASS: 452 sbuf_cat(buf, config.default_class ? 453 config.default_class : ""); 454 break; 455 case _UC_MINUID: 456 sbuf_printf(buf, "%lu", (unsigned long) config.min_uid); 457 quote = 0; 458 break; 459 case _UC_MAXUID: 460 sbuf_printf(buf, "%lu", (unsigned long) config.max_uid); 461 quote = 0; 462 break; 463 case _UC_MINGID: 464 sbuf_printf(buf, "%lu", (unsigned long) config.min_gid); 465 quote = 0; 466 break; 467 case _UC_MAXGID: 468 sbuf_printf(buf, "%lu", (unsigned long) config.max_gid); 469 quote = 0; 470 break; 471 case _UC_EXPIRE: 472 sbuf_printf(buf, "%d", config.expire_days); 473 quote = 0; 474 break; 475 case _UC_PASSWORD: 476 sbuf_printf(buf, "%d", config.password_days); 477 quote = 0; 478 break; 479 case _UC_NONE: 480 break; 481 } 482 sbuf_finish(buf); 483 484 if (comments[i]) 485 fputs(comments[i], fp); 486 487 if (*kwds[i]) { 488 if (quote) 489 fprintf(fp, "%s = \"%s\"\n", kwds[i], 490 sbuf_data(buf)); 491 else 492 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf)); 493#if debugging 494 printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf)); 495#endif 496 } 497 } 498 sbuf_delete(buf); 499 return (fclose(fp) != EOF); 500} 501