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 }