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