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