PageRenderTime 142ms CodeModel.GetById 37ms app.highlight 95ms RepoModel.GetById 1ms app.codeStats 0ms

/usr/src/lib/libsmbfs/smb/ctx.c

https://bitbucket.org/osunix/osunix-gate
C | 1616 lines | 1121 code | 171 blank | 324 comment | 292 complexity | 3a2115365105468e7459a8049828b641 MD5 | raw file
   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: ctx.c,v 1.32.70.2 2005/06/02 00:55:40 lindak Exp $
  33 */
  34
  35/*
  36 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  37 */
  38
  39#include <sys/param.h>
  40#include <sys/ioctl.h>
  41#include <sys/time.h>
  42#include <sys/mount.h>
  43#include <sys/types.h>
  44#include <sys/byteorder.h>
  45
  46#include <fcntl.h>
  47#include <ctype.h>
  48#include <errno.h>
  49#include <stdio.h>
  50#include <string.h>
  51#include <strings.h>
  52#include <stdlib.h>
  53#include <pwd.h>
  54#include <grp.h>
  55#include <unistd.h>
  56#include <libintl.h>
  57#include <assert.h>
  58#include <nss_dbdefs.h>
  59
  60#include <cflib.h>
  61#include <netsmb/smb_lib.h>
  62#include <netsmb/netbios.h>
  63#include <netsmb/nb_lib.h>
  64#include <netsmb/smb_dev.h>
  65
  66#include "charsets.h"
  67#include "spnego.h"
  68#include "derparse.h"
  69#include "private.h"
  70#include "ntlm.h"
  71
  72#ifndef FALSE
  73#define	FALSE	0
  74#endif
  75#ifndef TRUE
  76#define	TRUE	1
  77#endif
  78
  79struct nv {
  80	char *name;
  81	int value;
  82};
  83
  84/* These two may be set by commands. */
  85int smb_debug, smb_verbose;
  86
  87/*
  88 * Was: STDPARAM_OPT - see smb_ctx_scan_argv, smb_ctx_opt
  89 */
  90const char smbutil_std_opts[] = "ABCD:E:I:L:M:NO:P:U:R:S:T:W:";
  91
  92/*
  93 * Give the RPC library a callback hook that will be
  94 * called whenever we destroy or reinit an smb_ctx_t.
  95 * The name rpc_cleanup_smbctx() is legacy, and was
  96 * originally a direct call into the RPC code.
  97 */
  98static smb_ctx_close_hook_t close_hook;
  99static void
 100rpc_cleanup_smbctx(struct smb_ctx *ctx)
 101{
 102	if (close_hook)
 103		(*close_hook)(ctx);
 104}
 105void
 106smb_ctx_set_close_hook(smb_ctx_close_hook_t hook)
 107{
 108	close_hook = hook;
 109}
 110
 111void
 112dump_ctx_flags(int flags)
 113{
 114	printf(" Flags: ");
 115	if (flags == 0)
 116		printf("0");
 117	if (flags & SMBCF_NOPWD)
 118		printf("NOPWD ");
 119	if (flags & SMBCF_SRIGHTS)
 120		printf("SRIGHTS ");
 121	if (flags & SMBCF_LOCALE)
 122		printf("LOCALE ");
 123	if (flags & SMBCF_CMD_DOM)
 124		printf("CMD_DOM ");
 125	if (flags & SMBCF_CMD_USR)
 126		printf("CMD_USR ");
 127	if (flags & SMBCF_CMD_PW)
 128		printf("CMD_PW ");
 129	if (flags & SMBCF_RESOLVED)
 130		printf("RESOLVED ");
 131	if (flags & SMBCF_KCBAD)
 132		printf("KCBAD ");
 133	if (flags & SMBCF_KCFOUND)
 134		printf("KCFOUND ");
 135	if (flags & SMBCF_BROWSEOK)
 136		printf("BROWSEOK ");
 137	if (flags & SMBCF_AUTHREQ)
 138		printf("AUTHREQ ");
 139	if (flags & SMBCF_KCSAVE)
 140		printf("KCSAVE  ");
 141	if (flags & SMBCF_XXX)
 142		printf("XXX ");
 143	if (flags & SMBCF_SSNACTIVE)
 144		printf("SSNACTIVE ");
 145	if (flags & SMBCF_KCDOMAIN)
 146		printf("KCDOMAIN ");
 147	printf("\n");
 148}
 149
 150void
 151dump_iod_ssn(smb_iod_ssn_t *is)
 152{
 153	static const char zeros[NTLM_HASH_SZ] = {0};
 154	struct smbioc_ossn *ssn = &is->iod_ossn;
 155
 156	printf(" ct_srvname=\"%s\", ", ssn->ssn_srvname);
 157	dump_sockaddr(&ssn->ssn_srvaddr.sa);
 158	printf(" dom=\"%s\", user=\"%s\"\n",
 159	    ssn->ssn_domain, ssn->ssn_user);
 160	printf(" ct_vopt=0x%x, ct_owner=%d\n",
 161	    ssn->ssn_vopt, ssn->ssn_owner);
 162	printf(" ct_authflags=0x%x\n", is->iod_authflags);
 163
 164	printf(" ct_nthash:");
 165	if (bcmp(zeros, &is->iod_nthash, NTLM_HASH_SZ))
 166		smb_hexdump(&is->iod_nthash, NTLM_HASH_SZ);
 167	else
 168		printf(" {0}\n");
 169
 170	printf(" ct_lmhash:");
 171	if (bcmp(zeros, &is->iod_lmhash, NTLM_HASH_SZ))
 172		smb_hexdump(&is->iod_lmhash, NTLM_HASH_SZ);
 173	else
 174		printf(" {0}\n");
 175}
 176
 177void
 178dump_ctx(char *where, struct smb_ctx *ctx)
 179{
 180	printf("context %s:\n", where);
 181	dump_ctx_flags(ctx->ct_flags);
 182
 183	if (ctx->ct_locname)
 184		printf(" localname=\"%s\"", ctx->ct_locname);
 185	else
 186		printf(" localname=NULL");
 187
 188	if (ctx->ct_fullserver)
 189		printf(" fullserver=\"%s\"", ctx->ct_fullserver);
 190	else
 191		printf(" fullserver=NULL");
 192
 193	if (ctx->ct_srvaddr_s)
 194		printf(" srvaddr_s=\"%s\"\n", ctx->ct_srvaddr_s);
 195	else
 196		printf(" srvaddr_s=NULL\n");
 197
 198	if (ctx->ct_addrinfo)
 199		dump_addrinfo(ctx->ct_addrinfo);
 200	else
 201		printf(" ct_addrinfo = NULL\n");
 202
 203	dump_iod_ssn(&ctx->ct_iod_ssn);
 204
 205	printf(" share_name=\"%s\", share_type=%d\n",
 206	    ctx->ct_origshare ? ctx->ct_origshare : "",
 207	    ctx->ct_shtype_req);
 208
 209	/* dump_iod_work()? */
 210}
 211
 212int
 213smb_ctx_alloc(struct smb_ctx **ctx_pp)
 214{
 215	smb_ctx_t *ctx;
 216	int err;
 217
 218	ctx = malloc(sizeof (*ctx));
 219	if (ctx == NULL)
 220		return (ENOMEM);
 221	err = smb_ctx_init(ctx);
 222	if (err != 0) {
 223		free(ctx);
 224		return (err);
 225	}
 226	*ctx_pp = ctx;
 227	return (0);
 228}
 229
 230/*
 231 * Initialize an smb_ctx struct (defaults)
 232 */
 233int
 234smb_ctx_init(struct smb_ctx *ctx)
 235{
 236	char pwbuf[NSS_BUFLEN_PASSWD];
 237	struct passwd pw;
 238	int error = 0;
 239
 240	bzero(ctx, sizeof (*ctx));
 241
 242	error = nb_ctx_create(&ctx->ct_nb);
 243	if (error)
 244		return (error);
 245
 246	ctx->ct_dev_fd = -1;
 247	ctx->ct_door_fd = -1;
 248	ctx->ct_tran_fd = -1;
 249	ctx->ct_parsedlevel = SMBL_NONE;
 250	ctx->ct_minlevel = SMBL_NONE;
 251	ctx->ct_maxlevel = SMBL_PATH;
 252
 253	/* Fill in defaults */
 254	ctx->ct_vopt = SMBVOPT_EXT_SEC;
 255	ctx->ct_owner = SMBM_ANY_OWNER;
 256	ctx->ct_authflags = SMB_AT_DEFAULT;
 257	ctx->ct_minauth = SMB_AT_DEFAULT;
 258
 259	error = nb_ctx_setscope(ctx->ct_nb, "");
 260	if (error)
 261		return (error);
 262
 263	/*
 264	 * if the user name is not specified some other way,
 265	 * use the current user name (built-in default)
 266	 */
 267	if (getpwuid_r(getuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL) {
 268		error = smb_ctx_setuser(ctx, pw.pw_name, 0);
 269		if (error)
 270			return (error);
 271		ctx->ct_home = strdup(pw.pw_name);
 272		if (ctx->ct_home == NULL)
 273			return (ENOMEM);
 274	}
 275
 276	/*
 277	 * Set a built-in default domain (workgroup).
 278	 * Using the Windows/NT default for now.
 279	 */
 280	error = smb_ctx_setdomain(ctx, "WORKGROUP", 0);
 281	if (error)
 282		return (error);
 283
 284	return (error);
 285}
 286
 287/*
 288 * "Scan" the command line args to find the server name,
 289 * user name, and share name, as needed.  We need these
 290 * before reading the RC files and/or sharectl values.
 291 *
 292 * The sequence for getting all the members filled in
 293 * has some tricky aspects.  Here's how it works:
 294 *
 295 * The search order for options is as follows:
 296 *   command line options
 297 *   values parsed from UNC path (cmd)
 298 *   values from RC file (per-user)
 299 *   values from SMF (system-wide)
 300 *   built-in defaults
 301 *
 302 * Normally, one would simply get all the values starting with
 303 * the bottom of the above list and working to the top, and
 304 * overwriting values as you go.  But we need an exception.
 305 *
 306 * In this function, we parse the UNC path and command line options,
 307 * because we need (at least) the server name when we're getting the
 308 * SMF and RC file values.  However, values we get from the command
 309 * should not be overwritten by SMF or RC file parsing, so we mark
 310 * values from the command as "from CMD" and the RC file parser
 311 * leaves in place any values so marked.  See: SMBCF_CMD_*
 312 *
 313 * The semantics of these flags are: "This value came from the
 314 * current command instance, not from sources that may apply to
 315 * multiple commands."  (Different from the old "FROMUSR" flag.)
 316 *
 317 * Note that smb_ctx_opt() is called later to handle the
 318 * remaining options, which should be ignored here.
 319 * The (magic) leading ":" in cf_getopt() makes it
 320 * ignore options not in the options string.
 321 */
 322int
 323smb_ctx_scan_argv(struct smb_ctx *ctx, int argc, char **argv,
 324	int minlevel, int maxlevel, int sharetype)
 325{
 326	int  ind, opt, error = 0;
 327	int aflg = 0, uflg = 0;
 328	const char *arg;
 329
 330	/*
 331	 * Parse options, if any.  Values from here too
 332	 * are marked as "from CMD".
 333	 */
 334	if (argv == NULL)
 335		return (0);
 336
 337	ctx->ct_minlevel = minlevel;
 338	ctx->ct_maxlevel = maxlevel;
 339	ctx->ct_shtype_req = sharetype;
 340
 341	cf_opt_lock();
 342	/* Careful: no return/goto before cf_opt_unlock! */
 343	while (error == 0) {
 344		opt = cf_getopt(argc, argv, STDPARAM_OPT);
 345		if (opt == -1)
 346			break;
 347		arg = cf_optarg;
 348		/* NB: handle most in smb_ctx_opt */
 349		switch (opt) {
 350		case 'A':
 351			aflg = 1;
 352			error = smb_ctx_setuser(ctx, "", TRUE);
 353			ctx->ct_flags |= SMBCF_NOPWD;
 354			break;
 355		case 'U':
 356			uflg = 1;
 357			error = smb_ctx_setuser(ctx, arg, TRUE);
 358			break;
 359		default:
 360			DPRINT("skip opt=%c", opt);
 361			break;
 362		}
 363	}
 364	ind = cf_optind;
 365	arg = argv[ind];
 366	cf_optind = cf_optreset = 1;
 367	cf_opt_unlock();
 368
 369	if (error)
 370		return (error);
 371
 372	if (aflg && uflg)  {
 373		printf(gettext("-A and -U flags are exclusive.\n"));
 374		return (EINVAL);
 375	}
 376
 377	/*
 378	 * Parse the UNC path.  Values from here are
 379	 * marked as "from CMD".
 380	 */
 381	for (; ind < argc; ind++) {
 382		arg = argv[ind];
 383		if (strncmp(arg, "//", 2) != 0)
 384			continue;
 385		error = smb_ctx_parseunc(ctx, arg,
 386		    minlevel, maxlevel, sharetype, &arg);
 387		if (error)
 388			return (error);
 389		break;
 390	}
 391
 392	return (error);
 393}
 394
 395void
 396smb_ctx_free(smb_ctx_t *ctx)
 397{
 398	smb_ctx_done(ctx);
 399	free(ctx);
 400}
 401
 402void
 403smb_ctx_done(struct smb_ctx *ctx)
 404{
 405
 406	rpc_cleanup_smbctx(ctx);
 407
 408	if (ctx->ct_dev_fd != -1) {
 409		close(ctx->ct_dev_fd);
 410		ctx->ct_dev_fd = -1;
 411	}
 412	if (ctx->ct_door_fd != -1) {
 413		close(ctx->ct_door_fd);
 414		ctx->ct_door_fd = -1;
 415	}
 416	if (ctx->ct_tran_fd != -1) {
 417		close(ctx->ct_tran_fd);
 418		ctx->ct_tran_fd = -1;
 419	}
 420	if (ctx->ct_srvaddr_s) {
 421		free(ctx->ct_srvaddr_s);
 422		ctx->ct_srvaddr_s = NULL;
 423	}
 424	if (ctx->ct_nb) {
 425		nb_ctx_done(ctx->ct_nb);
 426		ctx->ct_nb = NULL;
 427	}
 428	if (ctx->ct_locname) {
 429		free(ctx->ct_locname);
 430		ctx->ct_locname = NULL;
 431	}
 432	if (ctx->ct_origshare) {
 433		free(ctx->ct_origshare);
 434		ctx->ct_origshare = NULL;
 435	}
 436	if (ctx->ct_fullserver) {
 437		free(ctx->ct_fullserver);
 438		ctx->ct_fullserver = NULL;
 439	}
 440	if (ctx->ct_addrinfo) {
 441		freeaddrinfo(ctx->ct_addrinfo);
 442		ctx->ct_addrinfo = NULL;
 443	}
 444	if (ctx->ct_home)
 445		free(ctx->ct_home);
 446	if (ctx->ct_srv_OS) {
 447		free(ctx->ct_srv_OS);
 448		ctx->ct_srv_OS = NULL;
 449	}
 450	if (ctx->ct_srv_LM) {
 451		free(ctx->ct_srv_LM);
 452		ctx->ct_srv_LM = NULL;
 453	}
 454	if (ctx->ct_mackey) {
 455		free(ctx->ct_mackey);
 456		ctx->ct_mackey = NULL;
 457	}
 458}
 459
 460static int
 461getsubstring(const char *p, uchar_t sep, char *dest, int maxlen,
 462    const char **next)
 463{
 464	int len;
 465
 466	maxlen--;
 467	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
 468		if (*p == 0)
 469			return (EINVAL);
 470		*dest = *p;
 471	}
 472	*dest = 0;
 473	*next = *p ? p + 1 : p;
 474	return (0);
 475}
 476
 477/*
 478 * Parse the UNC path.  Here we expect something like
 479 *   "//[workgroup;][user[:password]@]host[/share[/path]]"
 480 * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
 481 * Values found here are marked as "from CMD".
 482 */
 483int
 484smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc,
 485	int minlevel, int maxlevel, int sharetype,
 486	const char **next)
 487{
 488	const char *p = unc;
 489	char *p1, *colon;
 490	char tmp[1024];
 491	int error;
 492
 493	/*
 494	 * This may be called outside of _scan_argv,
 495	 * so make sure these get initialized.
 496	 */
 497	ctx->ct_minlevel = minlevel;
 498	ctx->ct_maxlevel = maxlevel;
 499	ctx->ct_shtype_req = sharetype;
 500
 501	ctx->ct_parsedlevel = SMBL_NONE;
 502	if (*p++ != '/' || *p++ != '/') {
 503		smb_error(dgettext(TEXT_DOMAIN,
 504		    "UNC should start with '//'"), 0);
 505		error = EINVAL;
 506		goto out;
 507	}
 508	p1 = tmp;
 509	error = getsubstring(p, ';', p1, sizeof (tmp), &p);
 510	if (!error) {
 511		if (*p1 == 0) {
 512			smb_error(dgettext(TEXT_DOMAIN,
 513			    "empty workgroup name"), 0);
 514			error = EINVAL;
 515			goto out;
 516		}
 517		error = smb_ctx_setdomain(ctx, unpercent(tmp), TRUE);
 518		if (error)
 519			goto out;
 520	}
 521	colon = (char *)p;
 522	error = getsubstring(p, '@', p1, sizeof (tmp), &p);
 523	if (!error) {
 524		if (ctx->ct_maxlevel < SMBL_VC) {
 525			smb_error(dgettext(TEXT_DOMAIN,
 526			    "no user name required"), 0);
 527			error = EINVAL;
 528			goto out;
 529		}
 530		p1 = strchr(tmp, ':');
 531		if (p1) {
 532			colon += p1 - tmp;
 533			*p1++ = (char)0;
 534			error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE);
 535			if (error)
 536				goto out;
 537			if (p - colon > 2)
 538				memset(colon+1, '*', p - colon - 2);
 539		}
 540		p1 = tmp;
 541		if (*p1 == 0) {
 542			smb_error(dgettext(TEXT_DOMAIN,
 543			    "empty user name"), 0);
 544			error = EINVAL;
 545			goto out;
 546		}
 547		error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE);
 548		if (error)
 549			goto out;
 550		ctx->ct_parsedlevel = SMBL_VC;
 551	}
 552	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
 553	if (error) {
 554		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
 555		if (error) {
 556			smb_error(dgettext(TEXT_DOMAIN,
 557			    "no server name found"), 0);
 558			goto out;
 559		}
 560	}
 561	if (*p1 == 0) {
 562		smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
 563		error = EINVAL;
 564		goto out;
 565	}
 566
 567	/*
 568	 * Save ct_fullserver without case conversion.
 569	 */
 570	if (strchr(tmp, '%'))
 571		(void) unpercent(tmp);
 572	error = smb_ctx_setfullserver(ctx, tmp);
 573	if (error)
 574		goto out;
 575
 576#ifdef	SMB_ST_NONE
 577	if (sharetype == SMB_ST_NONE) {
 578		if (next)
 579			*next = p;
 580		error = 0;
 581		goto out;
 582	}
 583#endif
 584
 585	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
 586		smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0);
 587		error = EINVAL;
 588		goto out;
 589	}
 590	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
 591	if (error) {
 592		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
 593		if (error) {
 594			smb_error(dgettext(TEXT_DOMAIN,
 595			    "unexpected end of line"), 0);
 596			goto out;
 597		}
 598	}
 599	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE &&
 600	    !(ctx->ct_flags & SMBCF_BROWSEOK)) {
 601		smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
 602		error = EINVAL;
 603		goto out;
 604	}
 605	if (next)
 606		*next = p;
 607	if (*p1 == 0) {
 608		error = 0;
 609		goto out;
 610	}
 611	error = smb_ctx_setshare(ctx, unpercent(p1), sharetype);
 612
 613out:
 614	if (error == 0 && smb_debug > 0)
 615		dump_ctx("after smb_ctx_parseunc", ctx);
 616
 617	return (error);
 618}
 619
 620#ifdef KICONV_SUPPORT
 621int
 622smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
 623{
 624	char *cp, *servercs, *localcs;
 625	int cslen = sizeof (ctx->ct_ssn.ioc_localcs);
 626	int scslen, lcslen, error;
 627
 628	cp = strchr(arg, ':');
 629	lcslen = cp ? (cp - arg) : 0;
 630	if (lcslen == 0 || lcslen >= cslen) {
 631		smb_error(dgettext(TEXT_DOMAIN,
 632		    "invalid local charset specification (%s)"), 0, arg);
 633		return (EINVAL);
 634	}
 635	scslen = (size_t)strlen(++cp);
 636	if (scslen == 0 || scslen >= cslen) {
 637		smb_error(dgettext(TEXT_DOMAIN,
 638		    "invalid server charset specification (%s)"), 0, arg);
 639		return (EINVAL);
 640	}
 641	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
 642	localcs[lcslen] = 0;
 643	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
 644	error = nls_setrecode(localcs, servercs);
 645	if (error == 0)
 646		return (0);
 647	smb_error(dgettext(TEXT_DOMAIN,
 648	    "can't initialize iconv support (%s:%s)"),
 649	    error, localcs, servercs);
 650	localcs[0] = 0;
 651	servercs[0] = 0;
 652	return (error);
 653}
 654#endif /* KICONV_SUPPORT */
 655
 656int
 657smb_ctx_setauthflags(struct smb_ctx *ctx, int flags)
 658{
 659	ctx->ct_authflags = flags;
 660	return (0);
 661}
 662
 663int
 664smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
 665{
 666	char *p = strdup(name);
 667
 668	if (p == NULL)
 669		return (ENOMEM);
 670	if (ctx->ct_fullserver)
 671		free(ctx->ct_fullserver);
 672	ctx->ct_fullserver = p;
 673	return (0);
 674}
 675
 676int
 677smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
 678{
 679	strlcpy(ctx->ct_srvname, name,
 680	    sizeof (ctx->ct_srvname));
 681	return (0);
 682}
 683
 684int
 685smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
 686{
 687
 688	if (strlen(name) >= sizeof (ctx->ct_user)) {
 689		smb_error(dgettext(TEXT_DOMAIN,
 690		    "user name '%s' too long"), 0, name);
 691		return (ENAMETOOLONG);
 692	}
 693
 694	/*
 695	 * Don't overwrite a value from the command line
 696	 * with one from anywhere else.
 697	 */
 698	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR))
 699		return (0);
 700
 701	strlcpy(ctx->ct_user, name,
 702	    sizeof (ctx->ct_user));
 703
 704	/* Mark this as "from the command line". */
 705	if (from_cmd)
 706		ctx->ct_flags |= SMBCF_CMD_USR;
 707
 708	return (0);
 709}
 710
 711/*
 712 * Don't overwrite a domain name from the
 713 * command line with one from anywhere else.
 714 * See smb_ctx_init() for notes about this.
 715 */
 716int
 717smb_ctx_setdomain(struct smb_ctx *ctx, const char *name, int from_cmd)
 718{
 719
 720	if (strlen(name) >= sizeof (ctx->ct_domain)) {
 721		smb_error(dgettext(TEXT_DOMAIN,
 722		    "workgroup name '%s' too long"), 0, name);
 723		return (ENAMETOOLONG);
 724	}
 725
 726	/*
 727	 * Don't overwrite a value from the command line
 728	 * with one from anywhere else.
 729	 */
 730	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
 731		return (0);
 732
 733	strlcpy(ctx->ct_domain, name,
 734	    sizeof (ctx->ct_domain));
 735
 736	/* Mark this as "from the command line". */
 737	if (from_cmd)
 738		ctx->ct_flags |= SMBCF_CMD_DOM;
 739
 740	return (0);
 741}
 742
 743int
 744smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
 745{
 746	int err;
 747
 748	if (passwd == NULL)
 749		return (EINVAL);
 750	if (strlen(passwd) >= sizeof (ctx->ct_password)) {
 751		smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
 752		return (ENAMETOOLONG);
 753	}
 754
 755	/*
 756	 * If called again after comand line parsing,
 757	 * don't overwrite a value from the command line
 758	 * with one from any stored config.
 759	 */
 760	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW))
 761		return (0);
 762
 763	memset(ctx->ct_password, 0, sizeof (ctx->ct_password));
 764	if (strncmp(passwd, "$$1", 3) == 0)
 765		(void) smb_simpledecrypt(ctx->ct_password, passwd);
 766	else
 767		strlcpy(ctx->ct_password, passwd,
 768		    sizeof (ctx->ct_password));
 769
 770	/*
 771	 * Compute LM hash, NT hash.
 772	 */
 773	if (ctx->ct_password[0]) {
 774		err = ntlm_compute_nt_hash(ctx->ct_nthash, ctx->ct_password);
 775		if (err != 0)
 776			return (err);
 777		err = ntlm_compute_lm_hash(ctx->ct_lmhash, ctx->ct_password);
 778		if (err != 0)
 779			return (err);
 780	}
 781
 782	/* Mark this as "from the command line". */
 783	if (from_cmd)
 784		ctx->ct_flags |= SMBCF_CMD_PW;
 785
 786	return (0);
 787}
 788
 789/*
 790 * Use this to set NTLM auth. info (hashes)
 791 * when we don't have the password.
 792 */
 793int
 794smb_ctx_setpwhash(smb_ctx_t *ctx,
 795    const uchar_t *nthash, const uchar_t *lmhash)
 796{
 797
 798	/* Need ct_password to be non-null. */
 799	if (ctx->ct_password[0] == '\0')
 800		strlcpy(ctx->ct_password, "$HASH",
 801		    sizeof (ctx->ct_password));
 802
 803	/*
 804	 * Compute LM hash, NT hash.
 805	 */
 806	memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
 807
 808	/* The LM hash is optional */
 809	if (lmhash) {
 810		memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
 811	}
 812
 813	return (0);
 814}
 815
 816int
 817smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
 818{
 819	if (strlen(share) >= SMBIOC_MAX_NAME) {
 820		smb_error(dgettext(TEXT_DOMAIN,
 821		    "share name '%s' too long"), 0, share);
 822		return (ENAMETOOLONG);
 823	}
 824	if (ctx->ct_origshare)
 825		free(ctx->ct_origshare);
 826	if ((ctx->ct_origshare = strdup(share)) == NULL)
 827		return (ENOMEM);
 828
 829	ctx->ct_shtype_req = stype;
 830
 831	return (0);
 832}
 833
 834int
 835smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
 836{
 837	if (addr == NULL || addr[0] == 0)
 838		return (EINVAL);
 839	if (ctx->ct_srvaddr_s)
 840		free(ctx->ct_srvaddr_s);
 841	if ((ctx->ct_srvaddr_s = strdup(addr)) == NULL)
 842		return (ENOMEM);
 843	return (0);
 844}
 845
 846/*
 847 * API for library caller to set signing enabled, required
 848 * Note: if not enable, ignore require
 849 */
 850int
 851smb_ctx_setsigning(struct smb_ctx *ctx, int enable, int require)
 852{
 853	ctx->ct_vopt &= ~SMBVOPT_SIGNING_MASK;
 854	if (enable) {
 855		ctx->ct_vopt |=	SMBVOPT_SIGNING_ENABLED;
 856		if (require)
 857			ctx->ct_vopt |=	SMBVOPT_SIGNING_REQUIRED;
 858	}
 859	return (0);
 860}
 861
 862static int
 863smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
 864{
 865	struct group gr;
 866	struct passwd pw;
 867	char buf[NSS_BUFLEN_PASSWD];
 868	char *cp;
 869
 870	cp = strchr(pair, ':');
 871	if (cp) {
 872		*cp++ = '\0';
 873		if (*cp && gid) {
 874			if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) {
 875				*gid = gr.gr_gid;
 876			} else
 877				smb_error(dgettext(TEXT_DOMAIN,
 878				    "Invalid group name %s, ignored"), 0, cp);
 879		}
 880	}
 881	if (*pair) {
 882		if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) {
 883			*uid = pw.pw_uid;
 884		} else
 885			smb_error(dgettext(TEXT_DOMAIN,
 886			    "Invalid user name %s, ignored"), 0, pair);
 887	}
 888
 889	return (0);
 890}
 891
 892/*
 893 * Suport a securty options arg, i.e. -S noext,lm,ntlm
 894 * for testing various type of authenticators.
 895 */
 896static struct nv
 897sectype_table[] = {
 898	/* noext - handled below */
 899	{ "anon",	SMB_AT_ANON },
 900	{ "lm",		SMB_AT_LM1 },
 901	{ "ntlm",	SMB_AT_NTLM1 },
 902	{ "ntlm2",	SMB_AT_NTLM2 },
 903	{ "krb5",	SMB_AT_KRB5 },
 904	{ NULL, 	0 },
 905};
 906int
 907smb_parse_secopts(struct smb_ctx *ctx, const char *arg)
 908{
 909	const char *sep = ":;,";
 910	const char *p = arg;
 911	struct nv *nv;
 912	int nlen, tlen;
 913	int authflags = 0;
 914
 915	for (;;) {
 916		/* skip separators */
 917		tlen = strspn(p, sep);
 918		p += tlen;
 919
 920		nlen = strcspn(p, sep);
 921		if (nlen == 0)
 922			break;
 923
 924		if (nlen == 5 && 0 == strncmp(p, "noext", nlen)) {
 925			/* Don't offer extended security. */
 926			ctx->ct_vopt &= ~SMBVOPT_EXT_SEC;
 927			p += nlen;
 928			continue;
 929		}
 930
 931		/* This is rarely called, so not optimized. */
 932		for (nv = sectype_table; nv->name; nv++) {
 933			tlen = strlen(nv->name);
 934			if (tlen == nlen && 0 == strncmp(p, nv->name, tlen))
 935				break;
 936		}
 937		if (nv->name == NULL) {
 938			smb_error(dgettext(TEXT_DOMAIN,
 939			    "%s: invalid security options"), 0, p);
 940			return (EINVAL);
 941		}
 942		authflags |= nv->value;
 943		p += nlen;
 944	}
 945
 946	if (authflags)
 947		ctx->ct_authflags = authflags;
 948
 949	return (0);
 950}
 951
 952/*
 953 * Commands use this with getopt.  See:
 954 *   STDPARAM_OPT, STDPARAM_ARGS
 955 * Called after smb_ctx_readrc().
 956 */
 957int
 958smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
 959{
 960	int error = 0;
 961	char *p, *cp;
 962	char tmp[1024];
 963
 964	switch (opt) {
 965	case 'A':
 966	case 'U':
 967		/* Handled in smb_ctx_init() */
 968		break;
 969	case 'I':
 970		error = smb_ctx_setsrvaddr(ctx, arg);
 971		break;
 972	case 'M':
 973		/* share connect rights - ignored */
 974		ctx->ct_flags |= SMBCF_SRIGHTS;
 975		break;
 976	case 'N':
 977		ctx->ct_flags |= SMBCF_NOPWD;
 978		break;
 979	case 'O':
 980		p = strdup(arg);
 981		cp = strchr(p, '/');
 982		if (cp)
 983			*cp = '\0';
 984		error = smb_parse_owner(cp, &ctx->ct_owner, NULL);
 985		free(p);
 986		break;
 987	case 'P':
 988/*		ctx->ct_vopt |= SMBCOPT_PERMANENT; */
 989		break;
 990	case 'R':
 991		/* retry count - ignored */
 992		break;
 993	case 'S':
 994		/* Security options (undocumented, just for tests) */
 995		error = smb_parse_secopts(ctx, arg);
 996		break;
 997	case 'T':
 998		/* timeout - ignored */
 999		break;
1000	case 'D':	/* domain */
1001	case 'W':	/* workgroup (legacy alias) */
1002		error = smb_ctx_setdomain(ctx, tmp, TRUE);
1003		break;
1004	}
1005	return (error);
1006}
1007
1008
1009/*
1010 * Original code injected iconv tables into the kernel.
1011 * Not sure if we'll need this or not...  REVISIT
1012 */
1013#ifdef KICONV_SUPPORT
1014static int
1015smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl)
1016{
1017	int error = 0;
1018
1019	error = kiconv_add_xlat_table(to, from, tbl);
1020	if (error && error != EEXIST) {
1021		smb_error(dgettext(TEXT_DOMAIN,
1022		    "can not setup kernel iconv table (%s:%s)"),
1023		    error, from, to);
1024		return (error);
1025	}
1026	return (error);
1027}
1028#endif	/* KICONV_SUPPORT */
1029
1030/*
1031 * Verify context info. before connect operation(s),
1032 * lookup specified server and try to fill all forgotten fields.
1033 * Legacy name used by commands.
1034 */
1035int
1036smb_ctx_resolve(struct smb_ctx *ctx)
1037{
1038	struct smbioc_ossn *ssn = &ctx->ct_ssn;
1039	int error = 0;
1040#ifdef KICONV_SUPPORT
1041	uchar_t cstbl[256];
1042	uint_t i;
1043#endif
1044
1045	if (smb_debug)
1046		dump_ctx("before smb_ctx_resolve", ctx);
1047
1048	ctx->ct_flags &= ~SMBCF_RESOLVED;
1049
1050	if (ctx->ct_fullserver == NULL) {
1051		smb_error(dgettext(TEXT_DOMAIN,
1052		    "no server name specified"), 0);
1053		return (EINVAL);
1054	}
1055
1056	if (ctx->ct_minlevel >= SMBL_SHARE &&
1057	    ctx->ct_origshare == NULL) {
1058		smb_error(dgettext(TEXT_DOMAIN,
1059		    "no share name specified for %s@%s"),
1060		    0, ssn->ssn_user, ctx->ct_fullserver);
1061		return (EINVAL);
1062	}
1063	error = nb_ctx_resolve(ctx->ct_nb);
1064	if (error)
1065		return (error);
1066#ifdef KICONV_SUPPORT
1067	if (ssn->ioc_localcs[0] == 0)
1068		strcpy(ssn->ioc_localcs, "default");	/* XXX: locale name ? */
1069	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
1070	if (error)
1071		return (error);
1072	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
1073	if (error)
1074		return (error);
1075	if (ssn->ioc_servercs[0] != 0) {
1076		for (i = 0; i < sizeof (cstbl); i++)
1077			cstbl[i] = i;
1078		nls_mem_toext(cstbl, cstbl, sizeof (cstbl));
1079		error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs,
1080		    cstbl);
1081		if (error)
1082			return (error);
1083		for (i = 0; i < sizeof (cstbl); i++)
1084			cstbl[i] = i;
1085		nls_mem_toloc(cstbl, cstbl, sizeof (cstbl));
1086		error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs,
1087		    cstbl);
1088		if (error)
1089			return (error);
1090	}
1091#endif	/* KICONV_SUPPORT */
1092
1093	/*
1094	 * Lookup the IP address and fill in ct_addrinfo.
1095	 *
1096	 * Note: smb_ctx_getaddr() returns a EAI_xxx
1097	 * error value like getaddrinfo(3), but this
1098	 * function needs to return an errno value.
1099	 */
1100	error = smb_ctx_getaddr(ctx);
1101	if (error) {
1102		const char *ais = gai_strerror(error);
1103		smb_error(dgettext(TEXT_DOMAIN,
1104		    "can't resolve name\"%s\", %s"),
1105		    0, ctx->ct_fullserver, ais);
1106		return (ENODATA);
1107	}
1108	assert(ctx->ct_addrinfo != NULL);
1109
1110	/*
1111	 * If we have a user name but no password,
1112	 * check for a keychain entry.
1113	 * XXX: Only for auth NTLM?
1114	 */
1115	if (ctx->ct_user[0] == '\0') {
1116		/*
1117		 * No user name (anonymous session).
1118		 * The minauth checks do not apply.
1119		 */
1120		ctx->ct_authflags = SMB_AT_ANON;
1121	} else {
1122		/*
1123		 * Have a user name.
1124		 * If we don't have a p/w yet,
1125		 * try the keychain.
1126		 */
1127		if (ctx->ct_password[0] == '\0')
1128			(void) smb_get_keychain(ctx);
1129		/*
1130		 * Mask out disallowed auth types.
1131		 */
1132		ctx->ct_authflags &= ctx->ct_minauth;
1133	}
1134	if (ctx->ct_authflags == 0) {
1135		smb_error(dgettext(TEXT_DOMAIN,
1136		    "no valid auth. types"), 0);
1137		return (ENOTSUP);
1138	}
1139
1140	ctx->ct_flags |= SMBCF_RESOLVED;
1141	if (smb_debug)
1142		dump_ctx("after smb_ctx_resolve", ctx);
1143
1144	return (0);
1145}
1146
1147int
1148smb_open_driver()
1149{
1150	int err, fd;
1151	uint32_t version;
1152
1153	fd = open("/dev/"NSMB_NAME, O_RDWR);
1154	if (fd < 0) {
1155		err = errno;
1156		smb_error(dgettext(TEXT_DOMAIN,
1157		    "failed to open driver"), err);
1158		return (-1);
1159	}
1160
1161	/*
1162	 * Check the driver version (paranoia)
1163	 * Do this BEFORE any other ioctl calls.
1164	 */
1165	if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
1166		version = 0;
1167	if (version != NSMB_VERSION) {
1168		smb_error(dgettext(TEXT_DOMAIN,
1169		    "incorrect driver version"), 0);
1170		close(fd);
1171		return (-1);
1172	}
1173
1174	/* This handle controls per-process resources. */
1175	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
1176
1177	return (fd);
1178}
1179
1180int
1181smb_ctx_gethandle(struct smb_ctx *ctx)
1182{
1183	int fd;
1184
1185	if (ctx->ct_dev_fd != -1) {
1186		rpc_cleanup_smbctx(ctx);
1187		close(ctx->ct_dev_fd);
1188		ctx->ct_dev_fd = -1;
1189		ctx->ct_flags &= ~SMBCF_SSNACTIVE;
1190	}
1191
1192	fd = smb_open_driver();
1193	if (fd < 0)
1194		return (ENODEV);
1195
1196	ctx->ct_dev_fd = fd;
1197	return (0);
1198}
1199
1200
1201/*
1202 * Find or create a connection + logon session
1203 */
1204int
1205smb_ctx_get_ssn(struct smb_ctx *ctx)
1206{
1207	int err = 0;
1208
1209	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
1210		return (EINVAL);
1211
1212	if (ctx->ct_dev_fd < 0) {
1213		if ((err = smb_ctx_gethandle(ctx)))
1214			return (err);
1215	}
1216
1217	/*
1218	 * Check whether the driver already has a VC
1219	 * we can use.  If so, we're done!
1220	 */
1221	err = smb_ctx_findvc(ctx);
1222	if (err == 0) {
1223		DPRINT("found an existing VC");
1224	} else {
1225		/*
1226		 * This calls the IOD to create a new session.
1227		 */
1228		DPRINT("setup a new VC");
1229		err = smb_ctx_newvc(ctx);
1230		if (err != 0)
1231			return (err);
1232
1233		/*
1234		 * Call findvc again.  The new VC sould be
1235		 * found in the driver this time.
1236		 */
1237		err = smb_ctx_findvc(ctx);
1238	}
1239
1240	return (err);
1241}
1242
1243/*
1244 * Get the string representation of a share "use" type,
1245 * as needed for the "service" in tree connect.
1246 */
1247static const char *
1248smb_use_type_str(smb_use_shtype_t stype)
1249{
1250	const char *pp;
1251
1252	switch (stype) {
1253	default:
1254	case USE_WILDCARD:
1255		pp = "?????";
1256		break;
1257	case USE_DISKDEV:
1258		pp = "A:";
1259		break;
1260	case USE_SPOOLDEV:
1261		pp = "LPT1:";
1262		break;
1263	case USE_CHARDEV:
1264		pp = "COMM";
1265		break;
1266	case USE_IPC:
1267		pp = "IPC";
1268		break;
1269	}
1270	return (pp);
1271}
1272
1273/*
1274 * Find or create a tree connection
1275 */
1276int
1277smb_ctx_get_tree(struct smb_ctx *ctx)
1278{
1279	smbioc_tcon_t *tcon = NULL;
1280	const char *stype;
1281	int cmd, err = 0;
1282
1283	if (ctx->ct_dev_fd < 0 ||
1284	    ctx->ct_origshare == NULL) {
1285		return (EINVAL);
1286	}
1287
1288	cmd = SMBIOC_TREE_CONNECT;
1289	tcon = malloc(sizeof (*tcon));
1290	if (tcon == NULL)
1291		return (ENOMEM);
1292	bzero(tcon, sizeof (*tcon));
1293	tcon->tc_flags = SMBLK_CREATE;
1294	tcon->tc_opt = 0;
1295
1296	/* The share name */
1297	strlcpy(tcon->tc_sh.sh_name, ctx->ct_origshare,
1298	    sizeof (tcon->tc_sh.sh_name));
1299
1300	/*
1301	 * Share password (unused - no share-level security)
1302	 * MS-SMB 2.2.6 says this should be null terminated,
1303	 * and the length includes the null.  Did bzero above,
1304	 * so just set length for the null.
1305	 */
1306	tcon->tc_sh.sh_pwlen = 1;
1307
1308	/* The share "use" type. */
1309	stype = smb_use_type_str(ctx->ct_shtype_req);
1310	strlcpy(tcon->tc_sh.sh_type_req, stype,
1311	    sizeof (tcon->tc_sh.sh_type_req));
1312
1313	/*
1314	 * Todo: share passwords for share-level security.
1315	 *
1316	 * The driver does the actual TCON call.
1317	 */
1318	if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
1319		err = errno;
1320		goto out;
1321	}
1322
1323	/*
1324	 * Check the returned share type
1325	 */
1326	DPRINT("ret. sh_type: \"%s\"", tcon->tc_sh.sh_type_ret);
1327	if (ctx->ct_shtype_req != USE_WILDCARD &&
1328	    0 != strcmp(stype, tcon->tc_sh.sh_type_ret)) {
1329		smb_error(dgettext(TEXT_DOMAIN,
1330		    "%s: incompatible share type"),
1331		    0, ctx->ct_origshare);
1332		err = EINVAL;
1333	}
1334
1335out:
1336	if (tcon != NULL)
1337		free(tcon);
1338
1339	return (err);
1340}
1341
1342/*
1343 * Return the hflags2 word for an smb_ctx.
1344 */
1345int
1346smb_ctx_flags2(struct smb_ctx *ctx)
1347{
1348	uint16_t flags2;
1349
1350	if (ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
1351		smb_error(dgettext(TEXT_DOMAIN,
1352		    "can't get flags2 for a session"), errno);
1353		return (-1);
1354	}
1355	return (flags2);
1356}
1357
1358/*
1359 * Get the transport level session key.
1360 * Must already have an active SMB session.
1361 */
1362int
1363smb_ctx_get_ssnkey(struct smb_ctx *ctx, uchar_t *key, size_t len)
1364{
1365	if (len < SMBIOC_HASH_SZ)
1366		return (EINVAL);
1367
1368	if (ioctl(ctx->ct_dev_fd, SMBIOC_GETSSNKEY, key) == -1)
1369		return (errno);
1370
1371	return (0);
1372}
1373
1374/*
1375 * RC file parsing stuff
1376 */
1377
1378static struct nv
1379minauth_table[] = {
1380	/* Allowed auth. types */
1381	{ "kerberos",	SMB_AT_KRB5 },
1382	{ "ntlmv2",	SMB_AT_KRB5|SMB_AT_NTLM2 },
1383	{ "ntlm",	SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1 },
1384	{ "lm",		SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1 },
1385	{ "none",	SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1|
1386			SMB_AT_ANON },
1387	{ NULL }
1388};
1389
1390
1391/*
1392 * level values:
1393 * 0 - default
1394 * 1 - server
1395 * 2 - server:user
1396 * 3 - server:user:share
1397 */
1398static int
1399smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
1400{
1401	char *p;
1402	int error;
1403
1404#ifdef	KICONV_SUPPORT
1405	if (level > 0) {
1406		rc_getstringptr(smb_rc, sname, "charsets", &p);
1407		if (p) {
1408			error = smb_ctx_setcharset(ctx, p);
1409			if (error)
1410				smb_error(dgettext(TEXT_DOMAIN,
1411	"charset specification in the section '%s' ignored"),
1412				    error, sname);
1413		}
1414	}
1415#endif
1416
1417	if (level <= 1) {
1418		/* Section is: [default] or [server] */
1419
1420		rc_getstringptr(smb_rc, sname, "minauth", &p);
1421		if (p) {
1422			/*
1423			 * "minauth" was set in this section; override
1424			 * the current minimum authentication setting.
1425			 */
1426			struct nv *nvp;
1427			for (nvp = minauth_table; nvp->name; nvp++)
1428				if (strcmp(p, nvp->name) == 0)
1429					break;
1430			if (nvp->name)
1431				ctx->ct_minauth = nvp->value;
1432			else {
1433				/*
1434				 * Unknown minimum authentication level.
1435				 */
1436				smb_error(dgettext(TEXT_DOMAIN,
1437"invalid minimum authentication level \"%s\" specified in the section %s"),
1438				    0, p, sname);
1439				return (EINVAL);
1440			}
1441		}
1442
1443		rc_getstringptr(smb_rc, sname, "signing", &p);
1444		if (p) {
1445			/*
1446			 * "signing" was set in this section; override
1447			 * the current signing settings.  Note:
1448			 * setsigning flags are: enable, require
1449			 */
1450			if (strcmp(p, "disabled") == 0) {
1451				(void) smb_ctx_setsigning(ctx, FALSE, FALSE);
1452			} else if (strcmp(p, "enabled") == 0) {
1453				(void) smb_ctx_setsigning(ctx, TRUE, FALSE);
1454			} else if (strcmp(p, "required") == 0) {
1455				(void) smb_ctx_setsigning(ctx, TRUE, TRUE);
1456			} else {
1457				/*
1458				 * Unknown "signing" value.
1459				 */
1460				smb_error(dgettext(TEXT_DOMAIN,
1461"invalid signing policy \"%s\" specified in the section %s"),
1462				    0, p, sname);
1463				return (EINVAL);
1464			}
1465		}
1466
1467		/*
1468		 * Domain name.  Allow both keywords:
1469		 * "workgroup", "domain"
1470		 *
1471		 * Note: these are NOT marked "from CMD".
1472		 * See long comment at smb_ctx_init()
1473		 */
1474		rc_getstringptr(smb_rc, sname, "workgroup", &p);
1475		if (p) {
1476			error = smb_ctx_setdomain(ctx, p, 0);
1477			if (error)
1478				smb_error(dgettext(TEXT_DOMAIN,
1479				    "workgroup specification in the "
1480				    "section '%s' ignored"), error, sname);
1481		}
1482		rc_getstringptr(smb_rc, sname, "domain", &p);
1483		if (p) {
1484			error = smb_ctx_setdomain(ctx, p, 0);
1485			if (error)
1486				smb_error(dgettext(TEXT_DOMAIN,
1487				    "domain specification in the "
1488				    "section '%s' ignored"), error, sname);
1489		}
1490
1491		rc_getstringptr(smb_rc, sname, "user", &p);
1492		if (p) {
1493			error = smb_ctx_setuser(ctx, p, 0);
1494			if (error)
1495				smb_error(dgettext(TEXT_DOMAIN,
1496				    "user specification in the "
1497				    "section '%s' ignored"), error, sname);
1498		}
1499	}
1500
1501	if (level == 1) {
1502		/* Section is: [server] */
1503		rc_getstringptr(smb_rc, sname, "addr", &p);
1504		if (p) {
1505			error = smb_ctx_setsrvaddr(ctx, p);
1506			if (error) {
1507				smb_error(dgettext(TEXT_DOMAIN,
1508				    "invalid address specified in section %s"),
1509				    0, sname);
1510				return (error);
1511			}
1512		}
1513	}
1514
1515	rc_getstringptr(smb_rc, sname, "password", &p);
1516	if (p) {
1517		error = smb_ctx_setpassword(ctx, p, 0);
1518		if (error)
1519			smb_error(dgettext(TEXT_DOMAIN,
1520	    "password specification in the section '%s' ignored"),
1521			    error, sname);
1522	}
1523
1524	return (0);
1525}
1526
1527/*
1528 * read rc file as follows:
1529 * 0: read [default] section
1530 * 1: override with [server] section
1531 * 2: override with [server:user] section
1532 * 3: override with [server:user:share] section
1533 * Since absence of rcfile is not fatal, silently ignore this fact.
1534 * smb_rc file should be closed by caller.
1535 */
1536int
1537smb_ctx_readrc(struct smb_ctx *ctx)
1538{
1539	char *home;
1540	char *sname = NULL;
1541	int sname_max;
1542	int err = 0;
1543
1544	if ((home = getenv("HOME")) == NULL)
1545		home = ctx->ct_home;
1546	if ((err = smb_open_rcfile(home)) != 0) {
1547		DPRINT("smb_open_rcfile, err=%d", err);
1548		/* ignore any error here */
1549		return (0);
1550	}
1551
1552	sname_max = 3 * SMBIOC_MAX_NAME + 4;
1553	sname = malloc(sname_max);
1554	if (sname == NULL) {
1555		err = ENOMEM;
1556		goto done;
1557	}
1558
1559	/*
1560	 * default parameters (level=0)
1561	 */
1562	smb_ctx_readrcsection(ctx, "default", 0);
1563	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
1564
1565	/*
1566	 * If we don't have a server name, we can't read any of the
1567	 * [server...] sections.
1568	 */
1569	if (ctx->ct_fullserver == NULL)
1570		goto done;
1571	/*
1572	 * SERVER parameters.
1573	 */
1574	smb_ctx_readrcsection(ctx, ctx->ct_fullserver, 1);
1575
1576	/*
1577	 * If we don't have a user name, we can't read any of the
1578	 * [server:user...] sections.
1579	 */
1580	if (ctx->ct_user[0] == 0)
1581		goto done;
1582	/*
1583	 * SERVER:USER parameters
1584	 */
1585	snprintf(sname, sname_max, "%s:%s",
1586	    ctx->ct_fullserver,
1587	    ctx->ct_user);
1588	smb_ctx_readrcsection(ctx, sname, 2);
1589
1590
1591	/*
1592	 * If we don't have a share name, we can't read any of the
1593	 * [server:user:share] sections.
1594	 */
1595	if (ctx->ct_origshare == NULL)
1596		goto done;
1597	/*
1598	 * SERVER:USER:SHARE parameters
1599	 */
1600	snprintf(sname, sname_max, "%s:%s:%s",
1601	    ctx->ct_fullserver,
1602	    ctx->ct_user,
1603	    ctx->ct_origshare);
1604	smb_ctx_readrcsection(ctx, sname, 3);
1605
1606done:
1607	if (sname)
1608		free(sname);
1609	smb_close_rcfile();
1610	if (smb_debug)
1611		dump_ctx("after smb_ctx_readrc", ctx);
1612	if (err)
1613		DPRINT("err=%d\n", err);
1614
1615	return (err);
1616}