pw_conf.c revision 20253
1281843Sdteske/*- 2120031Sscottl * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>. 3281843Sdteske * All rights reserved. 4115410Sscottl * 5222417Sjulian * Redistribution and use in source and binary forms, with or without 6115410Sscottl * modification, are permitted provided that the following conditions 7115410Sscottl * are met: 8115410Sscottl * 1. Redistributions of source code must retain the above copyright 9115410Sscottl * notice, this list of conditions and the following disclaimer as 10115410Sscottl * the first lines of this file unmodified. 11115410Sscottl * 2. Redistributions in binary form must reproduce the above copyright 12115410Sscottl * notice, this list of conditions and the following disclaimer in the 13115410Sscottl * documentation and/or other materials provided with the distribution. 14222417Sjulian * 3. All advertising materials mentioning features or use of this software 15115410Sscottl * must display the following acknowledgement: 16115410Sscottl * This product includes software developed by David L. Nugent. 17115410Sscottl * 4. The name of the author may not be used to endorse or promote products 18115410Sscottl * derived from this software without specific prior written permission. 19115410Sscottl * 20115410Sscottl * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND 21115410Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22115410Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23115410Sscottl * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE 24115410Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25115410Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26222417Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27115410Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28115410Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29115410Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30115410Sscottl * SUCH DAMAGE. 31281843Sdteske * 32262701Sdteske * $Id$ 33222417Sjulian */ 34222417Sjulian 35115410Sscottl#include <string.h> 36222417Sjulian#include <ctype.h> 37222417Sjulian#include <fcntl.h> 38222417Sjulian 39115410Sscottl#include "pw.h" 40222417Sjulian 41222417Sjulian#define debugging 0 42222417Sjulian 43222417Sjulianenum { 44281843Sdteske _UC_NONE, 45281843Sdteske _UC_DEFAULTPWD, 46281843Sdteske _UC_REUSEUID, 47222417Sjulian _UC_REUSEGID, 48222417Sjulian _UC_DOTDIR, 49222417Sjulian _UC_NEWMAIL, 50222417Sjulian _UC_LOGFILE, 51222417Sjulian _UC_HOMEROOT, 52281843Sdteske _UC_SHELLPATH, 53222417Sjulian _UC_SHELLS, 54222417Sjulian _UC_DEFAULTSHELL, 55281843Sdteske _UC_DEFAULTGROUP, 56115410Sscottl _UC_EXTRAGROUPS, 57281843Sdteske _UC_DEFAULTCLASS, 58281843Sdteske _UC_MINUID, 59281843Sdteske _UC_MAXUID, 60281843Sdteske _UC_MINGID, 61262703Sdteske _UC_MAXGID, 62281843Sdteske _UC_EXPIRE, 63281843Sdteske _UC_PASSWORD, 64281843Sdteske _UC_FIELDS 65281843Sdteske}; 66281843Sdteske 67281843Sdteskestatic char bourne_shell[] = "sh"; 68281843Sdteske 69281843Sdteskestatic char *system_shells[_UC_MAXSHELLS] = 70281843Sdteske{ 71115410Sscottl bourne_shell, 72281843Sdteske "csh" 73281843Sdteske}; 74115410Sscottl 75281843Sdteskestatic char *default_groups[_UC_MAXGROUPS] = 76281843Sdteske{ 77281843Sdteske NULL 78281843Sdteske}; 79281843Sdteske 80281843Sdteskestatic char const *booltrue[] = 81281843Sdteske{ 82115410Sscottl "yes", "true", "1", "on", NULL 83115410Sscottl}; 84115410Sscottlstatic char const *boolfalse[] = 85281843Sdteske{ 86222417Sjulian "no", "false", "0", "off", NULL 87222417Sjulian}; 88281843Sdteske 89116175Sscottlstatic struct userconf config = 90262701Sdteske{ 91294417Sroyger 0, /* Default password for new users? (nologin) */ 92262701Sdteske 0, /* Reuse uids? */ 93262701Sdteske 0, /* Reuse gids? */ 94262701Sdteske "/usr/share/skel", /* Where to obtain skeleton files */ 95262701Sdteske NULL, /* Mail to send to new accounts */ 96116175Sscottl "/var/log/userlog", /* Where to log changes */ 97281843Sdteske "/home", /* Where to create home directory */ 98222417Sjulian "/bin", /* Where shells are located */ 99281843Sdteske system_shells, /* List of shells (first is default) */ 100222417Sjulian bourne_shell, /* Default shell */ 101222417Sjulian NULL, /* Default group name */ 102115410Sscottl default_groups, /* Default (additional) groups */ 103222417Sjulian NULL, /* Default login class */ 104222417Sjulian 1000, 32000, /* Allowed range of uids */ 105222417Sjulian 1000, 32000, /* Allowed range of gids */ 106222417Sjulian 0, /* Days until account expires */ 107115410Sscottl 0 /* Days until password expires */ 108115410Sscottl}; 109262701Sdteske 110281843Sdteskestatic 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# Obtain default dotfiles from this directory\n", 117 "\n# Mail this file to new user (/etc/newuser.msg or no)\n", 118 "\n# Log add/change/remove information in this file\n", 119 "\n# Root directory in which $HOME directory is created\n", 120 "\n# Colon separated list of directories containing valid shells\n", 121 "\n# Space separated list of available shells (without paths)\n", 122 "\n# Default shell (without path)\n", 123 "\n# Default group (leave blank for new group per user)\n", 124 "\n# Extra groups for new users\n", 125 "\n# Default login class for new users\n", 126 "\n# Range of valid default user ids\n", 127 NULL, 128 "\n# Range of valid default group ids\n", 129 NULL, 130 "\n# Days after which account expires (0=disabled)\n", 131 "\n# Days after which password expires (0=disabled)\n" 132}; 133 134static char const *kwds[] = 135{ 136 "", 137 "defaultpasswd", 138 "reuseuids", 139 "reusegids", 140 "skeleton", 141 "newmail", 142 "logfile", 143 "home", 144 "shellpath", 145 "shells", 146 "defaultshell", 147 "defaultgroup", 148 "extragroups", 149 "defaultclass", 150 "minuid", 151 "maxuid", 152 "mingid", 153 "maxgid", 154 "expire_days", 155 "password_days", 156 NULL 157}; 158 159static char * 160unquote(char const * str) 161{ 162 if (str && (*str == '"' || *str == '\'')) { 163 char *p = strchr(str + 1, *str); 164 165 if (p != NULL) 166 *p = '\0'; 167 return (char *) (*++str ? str : NULL); 168 } 169 return (char *) str; 170} 171 172int 173boolean_val(char const * str, int dflt) 174{ 175 if ((str = unquote(str)) != NULL) { 176 int i; 177 178 for (i = 0; booltrue[i]; i++) 179 if (strcmp(str, booltrue[i]) == 0) 180 return 1; 181 for (i = 0; boolfalse[i]; i++) 182 if (strcmp(str, boolfalse[i]) == 0) 183 return 0; 184 185 /* 186 * Special cases for defaultpassword 187 */ 188 if (strcmp(str, "random") == 0) 189 return -1; 190 if (strcmp(str, "none") == 0) 191 return -2; 192 } 193 return dflt; 194} 195 196char const * 197boolean_str(int val) 198{ 199 if (val == -1) 200 return "random"; 201 else if (val == -2) 202 return "none"; 203 else 204 return val ? booltrue[0] : boolfalse[0]; 205} 206 207char * 208newstr(char const * p) 209{ 210 char *q = NULL; 211 212 if ((p = unquote(p)) != NULL) { 213 int l = strlen(p) + 1; 214 215 if ((q = malloc(l)) != NULL) 216 memcpy(q, p, l); 217 } 218 return q; 219} 220 221 222struct userconf * 223read_userconfig(char const * file) 224{ 225 FILE *fp; 226 227 if (file == NULL) 228 file = _PATH_PW_CONF; 229 if ((fp = fopen(file, "r")) != NULL) { 230 char buf[_UC_MAXLINE]; 231 232 while (fgets(buf, sizeof buf, fp) != NULL) { 233 char *p = strchr(buf, '\n'); 234 235 if (p == NULL) { /* Line too long */ 236 int ch; 237 238 while ((ch = fgetc(fp)) != '\n' && ch != EOF); 239 } else { 240 *p = '\0'; 241 if (*buf && *buf != '\n' && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') { 242 static char const toks[] = " \t\r\n,="; 243 char *q = strtok(NULL, toks); 244 int i = 0; 245 246 while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0) 247 ++i; 248#if debugging 249 if (i == _UC_FIELDS) 250 printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : ""); 251 else 252 printf("Got kwd[%s]=%s\n", p, q); 253#endif 254 switch (i) { 255 case _UC_DEFAULTPWD: 256 config.default_password = boolean_val(q, 1); 257 break; 258 case _UC_REUSEUID: 259 config.reuse_uids = boolean_val(q, 0); 260 break; 261 case _UC_REUSEGID: 262 config.reuse_gids = boolean_val(q, 0); 263 break; 264 case _UC_DOTDIR: 265 config.dotdir = (q == NULL || !boolean_val(q, 1)) 266 ? NULL : newstr(q); 267 break; 268 case _UC_NEWMAIL: 269 config.newmail = (q == NULL || !boolean_val(q, 1)) 270 ? NULL : newstr(q); 271 break; 272 case _UC_LOGFILE: 273 config.logfile = (q == NULL || !boolean_val(q, 1)) 274 ? NULL : newstr(q); 275 break; 276 case _UC_HOMEROOT: 277 config.home = (q == NULL || !boolean_val(q, 1)) 278 ? "/home" : newstr(q); 279 break; 280 case _UC_SHELLPATH: 281 config.shelldir = (q == NULL || !boolean_val(q, 1)) 282 ? "/bin" : newstr(q); 283 break; 284 case _UC_SHELLS: 285 for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks)) 286 system_shells[i] = newstr(q); 287 if (i > 0) 288 while (i < _UC_MAXSHELLS) 289 system_shells[i++] = NULL; 290 break; 291 case _UC_DEFAULTSHELL: 292 config.shell_default = (q == NULL || !boolean_val(q, 1)) 293 ? (char *) bourne_shell : newstr(q); 294 break; 295 case _UC_DEFAULTGROUP: 296 config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL) 297 ? NULL : newstr(q); 298 break; 299 case _UC_EXTRAGROUPS: 300 for (i = 0; i < _UC_MAXGROUPS && q != NULL; i++, q = strtok(NULL, toks)) 301 default_groups[i] = newstr(q); 302 if (i > 0) 303 while (i < _UC_MAXGROUPS) 304 default_groups[i++] = NULL; 305 break; 306 case _UC_DEFAULTCLASS: 307 config.default_class = (q == NULL || !boolean_val(q, 1)) 308 ? NULL : newstr(q); 309 break; 310 case _UC_MINUID: 311 if ((q = unquote(q)) != NULL && isdigit(*q)) 312 config.min_uid = (uid_t) atol(q); 313 break; 314 case _UC_MAXUID: 315 if ((q = unquote(q)) != NULL && isdigit(*q)) 316 config.max_uid = (uid_t) atol(q); 317 break; 318 case _UC_MINGID: 319 if ((q = unquote(q)) != NULL && isdigit(*q)) 320 config.min_gid = (gid_t) atol(q); 321 break; 322 case _UC_MAXGID: 323 if ((q = unquote(q)) != NULL && isdigit(*q)) 324 config.max_gid = (gid_t) atol(q); 325 break; 326 case _UC_EXPIRE: 327 if ((q = unquote(q)) != NULL && isdigit(*q)) 328 config.expire_days = atoi(q); 329 break; 330 case _UC_PASSWORD: 331 if ((q = unquote(q)) != NULL && isdigit(*q)) 332 config.password_days = atoi(q); 333 break; 334 case _UC_FIELDS: 335 case _UC_NONE: 336 break; 337 } 338 } 339 } 340 } 341 fclose(fp); 342 } 343 return &config; 344} 345 346 347int 348write_userconfig(char const * file) 349{ 350 int fd; 351 352 if (file == NULL) 353 file = _PATH_PW_CONF; 354 355 if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) { 356 FILE *fp; 357 358 if ((fp = fdopen(fd, "w")) == NULL) 359 close(fd); 360 else { 361 int i, j, k; 362 char buf[_UC_MAXLINE]; 363 364 for (i = _UC_NONE; i < _UC_FIELDS; i++) { 365 int quote = 1; 366 char const *val = buf; 367 368 *buf = '\0'; 369 switch (i) { 370 case _UC_DEFAULTPWD: 371 val = boolean_str(config.default_password); 372 break; 373 case _UC_REUSEUID: 374 val = boolean_str(config.reuse_uids); 375 break; 376 case _UC_REUSEGID: 377 val = boolean_str(config.reuse_gids); 378 break; 379 case _UC_DOTDIR: 380 val = config.dotdir ? config.dotdir : boolean_str(0); 381 break; 382 case _UC_NEWMAIL: 383 val = config.newmail ? config.newmail : boolean_str(0); 384 break; 385 case _UC_LOGFILE: 386 val = config.logfile ? config.logfile : boolean_str(0); 387 break; 388 case _UC_HOMEROOT: 389 val = config.home; 390 break; 391 case _UC_SHELLPATH: 392 val = config.shelldir; 393 break; 394 case _UC_SHELLS: 395 for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) 396 k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", system_shells[j]); 397 quote = 0; 398 break; 399 case _UC_DEFAULTSHELL: 400 val = config.shell_default ? config.shell_default : bourne_shell; 401 break; 402 case _UC_DEFAULTGROUP: 403 val = config.default_group ? config.default_group : ""; 404 break; 405 case _UC_EXTRAGROUPS: 406 for (j = k = 0; j < _UC_MAXGROUPS && default_groups[j] != NULL; j++) 407 k += sprintf(buf + k, "%s\"%s\"", k ? "," : "", default_groups[j]); 408 quote = 0; 409 break; 410 case _UC_DEFAULTCLASS: 411 val = config.default_class ? config.default_class : ""; 412 break; 413 case _UC_MINUID: 414 sprintf(buf, "%lu", (unsigned long) config.min_uid); 415 quote = 0; 416 break; 417 case _UC_MAXUID: 418 sprintf(buf, "%lu", (unsigned long) config.max_uid); 419 quote = 0; 420 break; 421 case _UC_MINGID: 422 sprintf(buf, "%lu", (unsigned long) config.min_gid); 423 quote = 0; 424 break; 425 case _UC_MAXGID: 426 sprintf(buf, "%lu", (unsigned long) config.max_gid); 427 quote = 0; 428 break; 429 case _UC_EXPIRE: 430 sprintf(buf, "%d", config.expire_days); 431 quote = 0; 432 break; 433 case _UC_PASSWORD: 434 sprintf(buf, "%d", config.password_days); 435 quote = 0; 436 break; 437 case _UC_NONE: 438 break; 439 } 440 441 if (comments[i]) 442 fputs(comments[i], fp); 443 444 if (*kwds[i]) { 445 if (quote) 446 fprintf(fp, "%s = \"%s\"\n", kwds[i], val); 447 else 448 fprintf(fp, "%s = %s\n", kwds[i], val); 449#if debugging 450 printf("WROTE: %s = %s\n", kwds[i], val); 451#endif 452 } 453 } 454 return fclose(fp) != EOF; 455 } 456 } 457 return 0; 458} 459