Home | History | Annotate | Download | only in smb
      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