pw_conf.c revision 286150
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 286150 2015-08-01 09:55:47Z bapt $"; 30#endif /* not lint */ 31 32#include <sys/types.h> 33#include <sys/sbuf.h> 34#include <inttypes.h> 35#include <string.h> 36#include <ctype.h> 37#include <fcntl.h> 38#include <err.h> 39 40#include "pw.h" 41 42#define debugging 0 43 44enum { 45 _UC_NONE, 46 _UC_DEFAULTPWD, 47 _UC_REUSEUID, 48 _UC_REUSEGID, 49 _UC_NISPASSWD, 50 _UC_DOTDIR, 51 _UC_NEWMAIL, 52 _UC_LOGFILE, 53 _UC_HOMEROOT, 54 _UC_HOMEMODE, 55 _UC_SHELLPATH, 56 _UC_SHELLS, 57 _UC_DEFAULTSHELL, 58 _UC_DEFAULTGROUP, 59 _UC_EXTRAGROUPS, 60 _UC_DEFAULTCLASS, 61 _UC_MINUID, 62 _UC_MAXUID, 63 _UC_MINGID, 64 _UC_MAXGID, 65 _UC_EXPIRE, 66 _UC_PASSWORD, 67 _UC_FIELDS 68}; 69 70static char bourne_shell[] = "sh"; 71 72static char *system_shells[_UC_MAXSHELLS] = 73{ 74 bourne_shell, 75 "csh", 76 "tcsh" 77}; 78 79static char const *booltrue[] = 80{ 81 "yes", "true", "1", "on", NULL 82}; 83static char const *boolfalse[] = 84{ 85 "no", "false", "0", "off", NULL 86}; 87 88static struct userconf config = 89{ 90 0, /* Default password for new users? (nologin) */ 91 0, /* Reuse uids? */ 92 0, /* Reuse gids? */ 93 NULL, /* NIS version of the passwd file */ 94 "/usr/share/skel", /* Where to obtain skeleton files */ 95 NULL, /* Mail to send to new accounts */ 96 "/var/log/userlog", /* Where to log changes */ 97 "/home", /* Where to create home directory */ 98 _DEF_DIRMODE, /* Home directory perms, modified by umask */ 99 "/bin", /* Where shells are located */ 100 system_shells, /* List of shells (first is default) */ 101 bourne_shell, /* Default shell */ 102 NULL, /* Default group name */ 103 NULL, /* Default (additional) groups */ 104 NULL, /* Default login class */ 105 1000, 32000, /* Allowed range of uids */ 106 1000, 32000, /* Allowed range of gids */ 107 0, /* Days until account expires */ 108 0 /* Days until password expires */ 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.groups = sl_init(); 238 if (config.groups == NULL) 239 err(1, "sl_init()"); 240 if (file == NULL) 241 file = _PATH_PW_CONF; 242 243 if ((fp = fopen(file, "r")) == NULL) 244 return (&config); 245 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 sl_add(config.groups, newstr(q)); 320 break; 321 case _UC_DEFAULTCLASS: 322 config.default_class = (q == NULL || !boolean_val(q, 1)) 323 ? NULL : newstr(q); 324 break; 325 case _UC_MINUID: 326 if ((q = unquote(q)) != NULL && isdigit(*q)) 327 config.min_uid = (uid_t) atol(q); 328 break; 329 case _UC_MAXUID: 330 if ((q = unquote(q)) != NULL && isdigit(*q)) 331 config.max_uid = (uid_t) atol(q); 332 break; 333 case _UC_MINGID: 334 if ((q = unquote(q)) != NULL && isdigit(*q)) 335 config.min_gid = (gid_t) atol(q); 336 break; 337 case _UC_MAXGID: 338 if ((q = unquote(q)) != NULL && isdigit(*q)) 339 config.max_gid = (gid_t) atol(q); 340 break; 341 case _UC_EXPIRE: 342 if ((q = unquote(q)) != NULL && isdigit(*q)) 343 config.expire_days = atoi(q); 344 break; 345 case _UC_PASSWORD: 346 if ((q = unquote(q)) != NULL && isdigit(*q)) 347 config.password_days = atoi(q); 348 break; 349 case _UC_FIELDS: 350 case _UC_NONE: 351 break; 352 } 353 } 354 } 355 free(buf); 356 fclose(fp); 357 358 return (&config); 359} 360 361 362int 363write_userconfig(char const * file) 364{ 365 int fd; 366 int i, j; 367 struct sbuf *buf; 368 FILE *fp; 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 return (0); 375 376 if ((fp = fdopen(fd, "w")) == NULL) { 377 close(fd); 378 return (0); 379 } 380 381 buf = sbuf_new_auto(); 382 for (i = _UC_NONE; i < _UC_FIELDS; i++) { 383 int quote = 1; 384 385 sbuf_clear(buf); 386 switch (i) { 387 case _UC_DEFAULTPWD: 388 sbuf_cat(buf, boolean_str(config.default_password)); 389 break; 390 case _UC_REUSEUID: 391 sbuf_cat(buf, boolean_str(config.reuse_uids)); 392 break; 393 case _UC_REUSEGID: 394 sbuf_cat(buf, boolean_str(config.reuse_gids)); 395 break; 396 case _UC_NISPASSWD: 397 sbuf_cat(buf, config.nispasswd ? config.nispasswd : 398 ""); 399 quote = 0; 400 break; 401 case _UC_DOTDIR: 402 sbuf_cat(buf, config.dotdir ? config.dotdir : 403 boolean_str(0)); 404 break; 405 case _UC_NEWMAIL: 406 sbuf_cat(buf, config.newmail ? config.newmail : 407 boolean_str(0)); 408 break; 409 case _UC_LOGFILE: 410 sbuf_cat(buf, config.logfile ? config.logfile : 411 boolean_str(0)); 412 break; 413 case _UC_HOMEROOT: 414 sbuf_cat(buf, config.home); 415 break; 416 case _UC_HOMEMODE: 417 sbuf_printf(buf, "%04o", config.homemode); 418 quote = 0; 419 break; 420 case _UC_SHELLPATH: 421 sbuf_cat(buf, config.shelldir); 422 break; 423 case _UC_SHELLS: 424 for (j = 0; j < _UC_MAXSHELLS && 425 system_shells[j] != NULL; j++) 426 sbuf_printf(buf, "%s\"%s\"", j ? 427 "," : "", system_shells[j]); 428 quote = 0; 429 break; 430 case _UC_DEFAULTSHELL: 431 sbuf_cat(buf, config.shell_default ? 432 config.shell_default : bourne_shell); 433 break; 434 case _UC_DEFAULTGROUP: 435 sbuf_cat(buf, config.default_group ? 436 config.default_group : ""); 437 break; 438 case _UC_EXTRAGROUPS: 439 for (j = 0; config.groups != NULL && 440 j < (int)config.groups->sl_cur; j++) 441 sbuf_printf(buf, "%s\"%s\"", j ? 442 "," : "", config.groups->sl_str[j]); 443 quote = 0; 444 break; 445 case _UC_DEFAULTCLASS: 446 sbuf_cat(buf, config.default_class ? 447 config.default_class : ""); 448 break; 449 case _UC_MINUID: 450 sbuf_printf(buf, "%ju", (uintmax_t)config.min_uid); 451 quote = 0; 452 break; 453 case _UC_MAXUID: 454 sbuf_printf(buf, "%ju", (uintmax_t)config.max_uid); 455 quote = 0; 456 break; 457 case _UC_MINGID: 458 sbuf_printf(buf, "%ju", (uintmax_t)config.min_gid); 459 quote = 0; 460 break; 461 case _UC_MAXGID: 462 sbuf_printf(buf, "%ju", (uintmax_t)config.max_gid); 463 quote = 0; 464 break; 465 case _UC_EXPIRE: 466 sbuf_printf(buf, "%d", config.expire_days); 467 quote = 0; 468 break; 469 case _UC_PASSWORD: 470 sbuf_printf(buf, "%d", config.password_days); 471 quote = 0; 472 break; 473 case _UC_NONE: 474 break; 475 } 476 sbuf_finish(buf); 477 478 if (comments[i]) 479 fputs(comments[i], fp); 480 481 if (*kwds[i]) { 482 if (quote) 483 fprintf(fp, "%s = \"%s\"\n", kwds[i], 484 sbuf_data(buf)); 485 else 486 fprintf(fp, "%s = %s\n", kwds[i], sbuf_data(buf)); 487#if debugging 488 printf("WROTE: %s = %s\n", kwds[i], sbuf_data(buf)); 489#endif 490 } 491 } 492 sbuf_delete(buf); 493 return (fclose(fp) != EOF); 494} 495