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 strcmp(buf, "signing") == 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 SLIST_FOREACH(rsp, &smb_rc->rf_sect, rs_next) { 679 fprintf(stderr, "section=%s\n", rsp->rs_name); 680 fflush(stderr); 681 682 SLIST_FOREACH(rkp, &rsp->rs_keys, rk_next) { 683 fprintf(stderr, " key=%s, value=%s\n", 684 rkp->rk_name, rkp->rk_value); 685 fflush(stderr); 686 } 687 } 688 } 689 #endif 690 691 /* 692 * first parse "sharectl get smbfs, then $HOME/.nsmbrc 693 * This is called by library consumers (commands) 694 */ 695 int 696 smb_open_rcfile(char *home) 697 { 698 char *fn; 699 int len, error = 0; 700 701 mutex_lock(&rcfile_mutex); 702 703 smb_rc = NULL; 704 #if 0 /* before SMF */ 705 fn = SMB_CFG_FILE; 706 error = rc_open(fn, &smb_rc); 707 #else 708 fn = "(sharectl get smbfs)"; 709 error = rc_open_sharectl(&smb_rc); 710 #endif 711 if (error != 0 && error != ENOENT) { 712 /* Error from fopen. strerror is OK. */ 713 fprintf(stderr, dgettext(TEXT_DOMAIN, 714 "Can't open %s: %s\n"), fn, strerror(errno)); 715 } 716 #ifdef DEBUG 717 if (smb_debug) 718 dump_props(fn); 719 #endif 720 721 if (home) { 722 len = strlen(home) + 20; 723 fn = malloc(len); 724 snprintf(fn, len, "%s/.nsmbrc", home); 725 home_nsmbrc = 1; 726 error = rc_merge(fn, &smb_rc); 727 if (error != 0 && error != ENOENT) { 728 fprintf(stderr, dgettext(TEXT_DOMAIN, 729 "Can't open %s: %s\n"), fn, strerror(errno)); 730 } 731 home_nsmbrc = 0; 732 #ifdef DEBUG 733 if (smb_debug) 734 dump_props(fn); 735 #endif 736 free(fn); 737 } 738 739 /* Mostly ignore error returns above. */ 740 if (smb_rc == NULL) 741 error = ENOENT; 742 else 743 error = 0; 744 745 mutex_unlock(&rcfile_mutex); 746 747 return (error); 748 } 749 750 /* 751 * This is called by library consumers (commands) 752 */ 753 void 754 smb_close_rcfile(void) 755 { 756 struct rcfile *rcp; 757 758 if ((rcp = smb_rc) != NULL) { 759 smb_rc = NULL; 760 rc_close(rcp); 761 } 762 } 763