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