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