1 /* 2 * Copyright (c) 2000, Boris Popov 3 * 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 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Boris Popov. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 * 32 * $Id: rcfile.c,v 1.1.1.2 2001/07/06 22:38:43 conrad Exp $ 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <fcntl.h> 38 #include <sys/types.h> 39 #include <sys/queue.h> 40 #include <sys/stat.h> 41 #include <ctype.h> 42 #include <errno.h> 43 #include <stdio.h> 44 #include <string.h> 45 #include <strings.h> 46 #include <stdlib.h> 47 #include <libintl.h> 48 #include <pwd.h> 49 #include <unistd.h> 50 #include <sys/debug.h> 51 52 #include <cflib.h> 53 #include "rcfile_priv.h" 54 55 SLIST_HEAD(rcfile_head, rcfile); 56 static struct rcfile_head pf_head = {NULL}; 57 58 static struct rcfile *rc_cachelookup(const char *filename); 59 struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname); 60 static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname); 61 static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp); 62 struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname); 63 static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, 64 const char *value); 65 static void rc_key_free(struct rckey *p); 66 static void rc_parse(struct rcfile *rcp); 67 68 int insecure_nsmbrc; 69 70 /* 71 * open rcfile and load its content, if already open - return previous handle 72 */ 73 int 74 rc_open(const char *filename, const char *mode, struct rcfile **rcfile) 75 { 76 struct rcfile *rcp; 77 FILE *f; 78 struct stat statbuf; 79 80 rcp = rc_cachelookup(filename); 81 if (rcp) { 82 *rcfile = rcp; 83 return (0); 84 } 85 f = fopen(filename, mode); 86 if (f == NULL) 87 return (errno); 88 insecure_nsmbrc = 0; 89 if (fstat(fileno(f), &statbuf) >= 0 && 90 (statbuf.st_mode & 077) != 0) 91 insecure_nsmbrc = 1; 92 rcp = malloc(sizeof (struct rcfile)); 93 if (rcp == NULL) { 94 fclose(f); 95 return (ENOMEM); 96 } 97 bzero(rcp, sizeof (struct rcfile)); 98 rcp->rf_name = strdup(filename); 99 rcp->rf_f = f; 100 SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); 101 rc_parse(rcp); 102 *rcfile = rcp; 103 return (0); 104 } 105 106 int 107 rc_merge(const char *filename, struct rcfile **rcfile) 108 { 109 struct rcfile *rcp = *rcfile; 110 FILE *f, *t; 111 112 insecure_nsmbrc = 0; 113 if (rcp == NULL) { 114 return (rc_open(filename, "r", rcfile)); 115 } 116 f = fopen(filename, "r"); 117 if (f == NULL) 118 return (errno); 119 t = rcp->rf_f; 120 rcp->rf_f = f; 121 rc_parse(rcp); 122 rcp->rf_f = t; 123 fclose(f); 124 return (0); 125 } 126 127 int 128 rc_merge_pipe(const char *command, struct rcfile **rcfile) 129 { 130 struct rcfile *rcp = *rcfile; 131 FILE *f, *t; 132 133 insecure_nsmbrc = 0; 134 f = popen(command, "r"); 135 if (f == NULL) 136 return (errno); 137 if (rcp == NULL) { 138 rcp = malloc(sizeof (struct rcfile)); 139 if (rcp == NULL) { 140 fclose(f); 141 return (ENOMEM); 142 } 143 *rcfile = rcp; 144 bzero(rcp, sizeof (struct rcfile)); 145 rcp->rf_name = strdup(command); 146 rcp->rf_f = f; 147 SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); 148 rc_parse(rcp); 149 } else { 150 t = rcp->rf_f; 151 rcp->rf_f = f; 152 rc_parse(rcp); 153 rcp->rf_f = t; 154 } 155 fclose(f); 156 return (0); 157 } 158 159 int 160 rc_close(struct rcfile *rcp) 161 { 162 struct rcsection *p, *n; 163 164 fclose(rcp->rf_f); 165 for (p = SLIST_FIRST(&rcp->rf_sect); p; ) { 166 n = p; 167 p = SLIST_NEXT(p, rs_next); 168 rc_freesect(rcp, n); 169 } 170 free(rcp->rf_name); 171 SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next); 172 free(rcp); 173 return (0); 174 } 175 176 static struct rcfile * 177 rc_cachelookup(const char *filename) 178 { 179 struct rcfile *p; 180 181 SLIST_FOREACH(p, &pf_head, rf_next) 182 if (strcmp(filename, p->rf_name) == 0) 183 return (p); 184 return (0); 185 } 186 187 /* static */ struct rcsection * 188 rc_findsect(struct rcfile *rcp, const char *sectname) 189 { 190 struct rcsection *p; 191 192 SLIST_FOREACH(p, &rcp->rf_sect, rs_next) 193 if (strcasecmp(p->rs_name, sectname) == 0) 194 return (p); 195 return (NULL); 196 } 197 198 static struct rcsection * 199 rc_addsect(struct rcfile *rcp, const char *sectname) 200 { 201 struct rcsection *p; 202 203 p = rc_findsect(rcp, sectname); 204 if (p) 205 return (p); 206 p = malloc(sizeof (*p)); 207 if (!p) 208 return (NULL); 209 p->rs_name = strdup(sectname); 210 SLIST_INIT(&p->rs_keys); 211 SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next); 212 return (p); 213 } 214 215 static int 216 rc_freesect(struct rcfile *rcp, struct rcsection *rsp) 217 { 218 struct rckey *p, *n; 219 220 SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next); 221 for (p = SLIST_FIRST(&rsp->rs_keys); p; ) { 222 n = p; 223 p = SLIST_NEXT(p, rk_next); 224 rc_key_free(n); 225 } 226 free(rsp->rs_name); 227 free(rsp); 228 return (0); 229 } 230 231 /* static */ struct rckey * 232 rc_sect_findkey(struct rcsection *rsp, const char *keyname) 233 { 234 struct rckey *p; 235 236 SLIST_FOREACH(p, &rsp->rs_keys, rk_next) 237 if (strcmp(p->rk_name, keyname) == 0) 238 return (p); 239 return (NULL); 240 } 241 242 static struct rckey * 243 rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value) 244 { 245 struct rckey *p; 246 247 p = rc_sect_findkey(rsp, name); 248 if (!p) { 249 p = malloc(sizeof (*p)); 250 if (!p) 251 return (NULL); 252 SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next); 253 p->rk_name = strdup(name); 254 p->rk_value = value ? strdup(value) : strdup(""); 255 } 256 return (p); 257 } 258 259 #if 0 260 void 261 rc_sect_delkey(struct rcsection *rsp, struct rckey *p) 262 { 263 264 SLIST_REMOVE(&rsp->rs_keys, p, rckey, rk_next); 265 rc_key_free(p); 266 } 267 #endif 268 269 static void 270 rc_key_free(struct rckey *p) 271 { 272 free(p->rk_value); 273 free(p->rk_name); 274 free(p); 275 } 276 277 enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue}; 278 279 int home_nsmbrc = 0; 280 281 static char *minauth[] = { 282 "kerberos", 283 "ntlmv2", 284 "ntlm", 285 "lm", 286 "none", 287 NULL 288 }; 289 290 static int 291 eval_minauth(char *auth) 292 { 293 int i; 294 295 for (i = 0; minauth[i]; i++) 296 if (strcmp(auth, minauth[i]) == 0) 297 break; 298 return (i); 299 } 300 301 /* 302 * Ensure that "minauth" is set to the highest level (lowest array offset) 303 */ 304 static void 305 set_value(struct rcfile *rcp, struct rcsection *rsp, struct rckey *rkp, 306 char *ptr) 307 { 308 int now, new; 309 310 if (strcmp(rkp->rk_name, "minauth") == 0) { 311 now = eval_minauth(rkp->rk_value); 312 new = eval_minauth(ptr); 313 if (new >= now) { 314 #ifdef DEBUG 315 printf("set_value: rejecting %s=%s from %s\n", 316 rkp->rk_name, ptr, home_nsmbrc ? "user file" : "SMF"); 317 #endif 318 return; 319 } 320 } 321 #ifdef DEBUG 322 printf("set_value: applying %s=%s from %s\n", 323 rkp->rk_name, ptr, home_nsmbrc ? "user file" : "SMF"); 324 #endif 325 rkp->rk_value = strdup(ptr); 326 } 327 328 static void 329 rc_parse(struct rcfile *rcp) 330 { 331 FILE *f = rcp->rf_f; 332 int state = stNewLine, c; 333 struct rcsection *rsp = NULL; 334 struct rckey *rkp = NULL; 335 char buf[2048]; 336 char *next = buf, *last = &buf[sizeof (buf)-1]; 337 338 while ((c = getc(f)) != EOF) { 339 if (c == '\r') 340 continue; 341 if (state == stNewLine) { 342 next = buf; 343 if (isspace(c)) 344 continue; /* skip leading junk */ 345 if (c == '[') { 346 state = stHeader; 347 rsp = NULL; 348 continue; 349 } 350 if (c == '#' || c == ';') { 351 state = stSkipToEOL; 352 } else { /* something meaningfull */ 353 state = stGetKey; 354 } 355 } 356 /* ignore long lines */ 357 if (state == stSkipToEOL || next == last) { 358 if (c == '\n') { 359 state = stNewLine; 360 next = buf; 361 } 362 continue; 363 } 364 if (state == stHeader) { 365 if (c == ']') { 366 *next = 0; 367 next = buf; 368 rsp = rc_addsect(rcp, buf); 369 state = stSkipToEOL; 370 } else 371 *next++ = c; 372 continue; 373 } 374 if (state == stGetKey) { 375 /* side effect: 'key name=' */ 376 if (c == ' ' || c == '\t') 377 continue; /* become 'keyname=' */ 378 if (c == '\n') { /* silently ignore ... */ 379 state = stNewLine; 380 continue; 381 } 382 if (c != '=') { 383 *next++ = c; 384 continue; 385 } 386 *next = 0; 387 if (rsp == NULL) { 388 fprintf(stderr, dgettext(TEXT_DOMAIN, 389 "Key '%s' defined before section\n"), buf); 390 state = stSkipToEOL; 391 continue; 392 } 393 if (home_nsmbrc && 394 (strcmp(buf, "nbns") == 0 || 395 strcmp(buf, "nbns_enable") == 0 || 396 strcmp(buf, "nbns_broadcast") == 0)) { 397 fprintf(stderr, dgettext(TEXT_DOMAIN, 398 "option %s may not be set " 399 "in user .nsmbrc file\n"), buf); 400 next = buf; 401 state = stNewLine; 402 continue; 403 } 404 if (insecure_nsmbrc && (strcmp(buf, "password") == 0)) { 405 fprintf(stderr, dgettext(TEXT_DOMAIN, 406 "Warning: .nsmbrc file not secure, " 407 "ignoring passwords\n")); 408 next = buf; 409 state = stNewLine; 410 continue; 411 } 412 rkp = rc_sect_addkey(rsp, buf, NULL); 413 next = buf; 414 state = stGetValue; 415 continue; 416 } 417 /* only stGetValue left */ 418 if (state != stGetValue) { 419 fprintf(stderr, dgettext(TEXT_DOMAIN, 420 "Well, I can't parse file '%s'\n"), rcp->rf_name); 421 state = stSkipToEOL; 422 } 423 if (c != '\n') { 424 *next++ = c; 425 continue; 426 } 427 *next = 0; 428 set_value(rcp, rsp, rkp, buf); 429 state = stNewLine; 430 rkp = NULL; 431 } /* while */ 432 if (c == EOF && state == stGetValue) { 433 *next = 0; 434 set_value(rcp, rsp, rkp, buf); 435 } 436 } 437 438 int 439 rc_getstringptr(struct rcfile *rcp, const char *section, const char *key, 440 char **dest) 441 { 442 struct rcsection *rsp; 443 struct rckey *rkp; 444 445 *dest = NULL; 446 rsp = rc_findsect(rcp, section); 447 if (!rsp) 448 return (ENOENT); 449 rkp = rc_sect_findkey(rsp, key); 450 if (!rkp) 451 return (ENOENT); 452 *dest = rkp->rk_value; 453 return (0); 454 } 455 456 int 457 rc_getstring(struct rcfile *rcp, const char *section, const char *key, 458 size_t maxlen, char *dest) 459 { 460 char *value; 461 int error; 462 463 error = rc_getstringptr(rcp, section, key, &value); 464 if (error) 465 return (error); 466 if (strlen(value) >= maxlen) { 467 fprintf(stdout, dgettext(TEXT_DOMAIN, 468 "line too long for key '%s' in section '%s', max = %d\n"), 469 key, section, maxlen); 470 return (EINVAL); 471 } 472 strcpy(dest, value); 473 return (0); 474 } 475 476 int 477 rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value) 478 { 479 struct rcsection *rsp; 480 struct rckey *rkp; 481 482 rsp = rc_findsect(rcp, section); 483 if (!rsp) 484 return (ENOENT); 485 rkp = rc_sect_findkey(rsp, key); 486 if (!rkp) 487 return (ENOENT); 488 errno = 0; 489 *value = strtol(rkp->rk_value, NULL, 0); 490 if (errno) { 491 fprintf(stdout, dgettext(TEXT_DOMAIN, 492 "invalid int value '%s' for key '%s' in section '%s'\n"), 493 rkp->rk_value, key, section); 494 return (errno); 495 } 496 return (0); 497 } 498 499 /* 500 * 1,yes,true 501 * 0,no,false 502 */ 503 int 504 rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value) 505 { 506 struct rcsection *rsp; 507 struct rckey *rkp; 508 char *p; 509 510 rsp = rc_findsect(rcp, section); 511 if (!rsp) 512 return (ENOENT); 513 rkp = rc_sect_findkey(rsp, key); 514 if (!rkp) 515 return (ENOENT); 516 p = rkp->rk_value; 517 while (*p && isspace(*p)) p++; 518 if (*p == '0' || 519 strcasecmp(p, "no") == 0 || 520 strcasecmp(p, "false") == 0) { 521 *value = 0; 522 return (0); 523 } 524 if (*p == '1' || 525 strcasecmp(p, "yes") == 0 || 526 strcasecmp(p, "true") == 0) { 527 *value = 1; 528 return (0); 529 } 530 fprintf(stderr, dgettext(TEXT_DOMAIN, 531 "invalid boolean value '%s' for key '%s' in section '%s' \n"), 532 p, key, section); 533 return (EINVAL); 534 } 535 536 /* 537 * Unified command line/rc file parser 538 */ 539 int 540 opt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect, 541 opt_callback_t *callback) 542 { 543 int len, error; 544 545 for (; ap->opt; ap++) { 546 switch (ap->type) { 547 case OPTARG_STR: 548 if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0) 549 break; 550 len = strlen(ap->str); 551 if (len > ap->ival) { 552 fprintf(stdout, dgettext(TEXT_DOMAIN, 553 "rc: argument for option '%c' (%s) too long\n"), 554 ap->opt, ap->name); 555 return (EINVAL); 556 } 557 callback(ap); 558 break; 559 case OPTARG_BOOL: 560 error = rc_getbool(rcp, sect, ap->name, &ap->ival); 561 if (error == ENOENT) 562 break; 563 if (error) 564 return (EINVAL); 565 callback(ap); 566 break; 567 case OPTARG_INT: 568 if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0) 569 break; 570 if (((ap->flag & OPTFL_HAVEMIN) && 571 ap->ival < ap->min) || 572 ((ap->flag & OPTFL_HAVEMAX) && 573 ap->ival > ap->max)) { 574 fprintf(stdout, dgettext(TEXT_DOMAIN, 575 "rc: argument for option '%c' (%s) " 576 "should be in [%d-%d] range\n"), 577 ap->opt, ap->name, ap->min, ap->max); 578 return (EINVAL); 579 } 580 callback(ap); 581 break; 582 default: 583 break; 584 } 585 } 586 return (0); 587 } 588 589 int 590 opt_args_parseopt(struct opt_args *ap, int opt, char *arg, 591 opt_callback_t *callback) 592 { 593 int len; 594 595 for (; ap->opt; ap++) { 596 if (ap->opt != opt) 597 continue; 598 switch (ap->type) { 599 case OPTARG_STR: 600 ap->str = arg; 601 if (arg) { 602 len = strlen(ap->str); 603 if (len > ap->ival) { 604 fprintf(stdout, dgettext(TEXT_DOMAIN, 605 "opt: Argument for option '%c' (%s) too long\n"), 606 ap->opt, ap->name); 607 return (EINVAL); 608 } 609 callback(ap); 610 } 611 break; 612 case OPTARG_BOOL: 613 ap->ival = 0; 614 callback(ap); 615 break; 616 case OPTARG_INT: 617 errno = 0; 618 ap->ival = strtol(arg, NULL, 0); 619 if (errno) { 620 fprintf(stdout, dgettext(TEXT_DOMAIN, 621 "opt: Invalid integer value for " 622 "option '%c' (%s).\n"), 623 ap->opt, ap->name); 624 return (EINVAL); 625 } 626 if (((ap->flag & OPTFL_HAVEMIN) && 627 (ap->ival < ap->min)) || 628 ((ap->flag & OPTFL_HAVEMAX) && 629 (ap->ival > ap->max))) { 630 fprintf(stdout, dgettext(TEXT_DOMAIN, 631 "opt: Argument for option '%c' (%s) " 632 "should be in [%d-%d] range\n"), 633 ap->opt, ap->name, ap->min, ap->max); 634 return (EINVAL); 635 } 636 callback(ap); 637 break; 638 default: 639 break; 640 } 641 break; 642 } 643 return (0); 644 } 645