xref: /illumos-gate/usr/src/lib/libsmbfs/smb/ctx.c (revision 9c9af2590af49bb395bc8d2eace0f2d4ea16d165)
14bff34e3Sthurlow /*
24bff34e3Sthurlow  * Copyright (c) 2000, Boris Popov
34bff34e3Sthurlow  * All rights reserved.
44bff34e3Sthurlow  *
54bff34e3Sthurlow  * Redistribution and use in source and binary forms, with or without
64bff34e3Sthurlow  * modification, are permitted provided that the following conditions
74bff34e3Sthurlow  * are met:
84bff34e3Sthurlow  * 1. Redistributions of source code must retain the above copyright
94bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer.
104bff34e3Sthurlow  * 2. Redistributions in binary form must reproduce the above copyright
114bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer in the
124bff34e3Sthurlow  *    documentation and/or other materials provided with the distribution.
134bff34e3Sthurlow  * 3. All advertising materials mentioning features or use of this software
144bff34e3Sthurlow  *    must display the following acknowledgement:
154bff34e3Sthurlow  *    This product includes software developed by Boris Popov.
164bff34e3Sthurlow  * 4. Neither the name of the author nor the names of any co-contributors
174bff34e3Sthurlow  *    may be used to endorse or promote products derived from this software
184bff34e3Sthurlow  *    without specific prior written permission.
194bff34e3Sthurlow  *
204bff34e3Sthurlow  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
214bff34e3Sthurlow  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224bff34e3Sthurlow  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234bff34e3Sthurlow  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
244bff34e3Sthurlow  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254bff34e3Sthurlow  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264bff34e3Sthurlow  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274bff34e3Sthurlow  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284bff34e3Sthurlow  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294bff34e3Sthurlow  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304bff34e3Sthurlow  * SUCH DAMAGE.
314bff34e3Sthurlow  *
324bff34e3Sthurlow  * $Id: ctx.c,v 1.32.70.2 2005/06/02 00:55:40 lindak Exp $
334bff34e3Sthurlow  */
344bff34e3Sthurlow 
35*9c9af259SGordon Ross /*
36*9c9af259SGordon Ross  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
37*9c9af259SGordon Ross  * Use is subject to license terms.
38*9c9af259SGordon Ross  */
394bff34e3Sthurlow 
404bff34e3Sthurlow #include <sys/param.h>
414bff34e3Sthurlow #include <sys/ioctl.h>
424bff34e3Sthurlow #include <sys/time.h>
434bff34e3Sthurlow #include <sys/mount.h>
444bff34e3Sthurlow #include <sys/types.h>
454bff34e3Sthurlow #include <sys/byteorder.h>
464bff34e3Sthurlow 
474bff34e3Sthurlow #include <fcntl.h>
484bff34e3Sthurlow #include <ctype.h>
494bff34e3Sthurlow #include <errno.h>
504bff34e3Sthurlow #include <stdio.h>
514bff34e3Sthurlow #include <string.h>
524bff34e3Sthurlow #include <strings.h>
534bff34e3Sthurlow #include <stdlib.h>
544bff34e3Sthurlow #include <pwd.h>
554bff34e3Sthurlow #include <grp.h>
564bff34e3Sthurlow #include <unistd.h>
574bff34e3Sthurlow #include <libintl.h>
584bff34e3Sthurlow #include <assert.h>
594bff34e3Sthurlow #include <nss_dbdefs.h>
604bff34e3Sthurlow 
614bff34e3Sthurlow #include <kerberosv5/krb5.h>
624bff34e3Sthurlow #include <kerberosv5/com_err.h>
634bff34e3Sthurlow 
644bff34e3Sthurlow #include <netsmb/smb_lib.h>
654bff34e3Sthurlow #include <netsmb/netbios.h>
664bff34e3Sthurlow #include <netsmb/nb_lib.h>
674bff34e3Sthurlow #include <netsmb/smb_dev.h>
684bff34e3Sthurlow #include <cflib.h>
694bff34e3Sthurlow #include <charsets.h>
704bff34e3Sthurlow 
714bff34e3Sthurlow #include <spnego.h>
724bff34e3Sthurlow #include "derparse.h"
73*9c9af259SGordon Ross #include "private.h"
744bff34e3Sthurlow 
754bff34e3Sthurlow extern MECH_OID g_stcMechOIDList [];
764bff34e3Sthurlow 
774bff34e3Sthurlow #define	POWEROF2(x) (((x) & ((x)-1)) == 0)
784bff34e3Sthurlow 
794bff34e3Sthurlow /* These two may be set by commands. */
804bff34e3Sthurlow int smb_debug, smb_verbose;
814bff34e3Sthurlow 
824bff34e3Sthurlow /*
83*9c9af259SGordon Ross  * Give the RPC library a callback hook that will be
84*9c9af259SGordon Ross  * called whenever we destroy or reinit an smb_ctx_t.
85*9c9af259SGordon Ross  * The name rpc_cleanup_smbctx() is legacy, and was
86*9c9af259SGordon Ross  * originally a direct call into the RPC code.
874bff34e3Sthurlow  */
88*9c9af259SGordon Ross static smb_ctx_close_hook_t close_hook;
894bff34e3Sthurlow static void
904bff34e3Sthurlow rpc_cleanup_smbctx(struct smb_ctx *ctx)
914bff34e3Sthurlow {
92*9c9af259SGordon Ross 	if (close_hook)
93*9c9af259SGordon Ross 		(*close_hook)(ctx);
944bff34e3Sthurlow }
95*9c9af259SGordon Ross void
96*9c9af259SGordon Ross smb_ctx_set_close_hook(smb_ctx_close_hook_t hook)
97*9c9af259SGordon Ross {
98*9c9af259SGordon Ross 	close_hook = hook;
99*9c9af259SGordon Ross }
1004bff34e3Sthurlow 
1014bff34e3Sthurlow void
1024bff34e3Sthurlow dump_ctx_flags(int flags)
1034bff34e3Sthurlow {
1044bff34e3Sthurlow 	printf(" Flags: ");
1054bff34e3Sthurlow 	if (flags == 0)
1064bff34e3Sthurlow 		printf("0");
1074bff34e3Sthurlow 	if (flags & SMBCF_NOPWD)
1084bff34e3Sthurlow 		printf("NOPWD ");
1094bff34e3Sthurlow 	if (flags & SMBCF_SRIGHTS)
1104bff34e3Sthurlow 		printf("SRIGHTS ");
1114bff34e3Sthurlow 	if (flags & SMBCF_LOCALE)
1124bff34e3Sthurlow 		printf("LOCALE ");
1134bff34e3Sthurlow 	if (flags & SMBCF_CMD_DOM)
1144bff34e3Sthurlow 		printf("CMD_DOM ");
1154bff34e3Sthurlow 	if (flags & SMBCF_CMD_USR)
1164bff34e3Sthurlow 		printf("CMD_USR ");
1174bff34e3Sthurlow 	if (flags & SMBCF_CMD_PW)
1184bff34e3Sthurlow 		printf("CMD_PW ");
1194bff34e3Sthurlow 	if (flags & SMBCF_RESOLVED)
1204bff34e3Sthurlow 		printf("RESOLVED ");
1214bff34e3Sthurlow 	if (flags & SMBCF_KCBAD)
1224bff34e3Sthurlow 		printf("KCBAD ");
1234bff34e3Sthurlow 	if (flags & SMBCF_KCFOUND)
1244bff34e3Sthurlow 		printf("KCFOUND ");
1254bff34e3Sthurlow 	if (flags & SMBCF_BROWSEOK)
1264bff34e3Sthurlow 		printf("BROWSEOK ");
1274bff34e3Sthurlow 	if (flags & SMBCF_AUTHREQ)
1284bff34e3Sthurlow 		printf("AUTHREQ ");
1294bff34e3Sthurlow 	if (flags & SMBCF_KCSAVE)
1304bff34e3Sthurlow 		printf("KCSAVE  ");
1314bff34e3Sthurlow 	if (flags & SMBCF_XXX)
1324bff34e3Sthurlow 		printf("XXX ");
1334bff34e3Sthurlow 	if (flags & SMBCF_SSNACTIVE)
1344bff34e3Sthurlow 		printf("SSNACTIVE ");
1354bff34e3Sthurlow 	if (flags & SMBCF_KCDOMAIN)
1364bff34e3Sthurlow 		printf("KCDOMAIN ");
1374bff34e3Sthurlow 	printf("\n");
1384bff34e3Sthurlow }
1394bff34e3Sthurlow 
1404bff34e3Sthurlow void
1414bff34e3Sthurlow dump_ctx_ssn(struct smbioc_ossn *ssn)
1424bff34e3Sthurlow {
1434bff34e3Sthurlow 	printf(" srvname=\"%s\", dom=\"%s\", user=\"%s\", password=%s\n",
1444bff34e3Sthurlow 	    ssn->ioc_srvname, ssn->ioc_workgroup, ssn->ioc_user,
1454bff34e3Sthurlow 	    ssn->ioc_password[0] ? "(non-null)" : "NULL");
1464bff34e3Sthurlow 	printf(" timeout=%d, retry=%d, owner=%d, group=%d\n",
1474bff34e3Sthurlow 	    ssn->ioc_timeout, ssn->ioc_retrycount,
1484bff34e3Sthurlow 	    ssn->ioc_owner, ssn->ioc_group);
1494bff34e3Sthurlow }
1504bff34e3Sthurlow 
1514bff34e3Sthurlow void
1524bff34e3Sthurlow dump_ctx_sh(struct smbioc_oshare *sh)
1534bff34e3Sthurlow {
1544bff34e3Sthurlow 	printf(" share_name=\"%s\", share_pw=\"%s\"\n",
1554bff34e3Sthurlow 	    sh->ioc_share, sh->ioc_password);
1564bff34e3Sthurlow }
1574bff34e3Sthurlow 
1584bff34e3Sthurlow void
1594bff34e3Sthurlow dump_ctx(char *where, struct smb_ctx *ctx)
1604bff34e3Sthurlow {
1614bff34e3Sthurlow 	printf("context %s:\n", where);
1624bff34e3Sthurlow 	dump_ctx_flags(ctx->ct_flags);
1634bff34e3Sthurlow 
1644bff34e3Sthurlow 	printf(" localname=\"%s\"", ctx->ct_locname);
1654bff34e3Sthurlow 
1664bff34e3Sthurlow 	if (ctx->ct_fullserver)
1674bff34e3Sthurlow 		printf(" fullserver=\"%s\"", ctx->ct_fullserver);
1684bff34e3Sthurlow 	else
1694bff34e3Sthurlow 		printf(" fullserver=NULL");
1704bff34e3Sthurlow 
1714bff34e3Sthurlow 	if (ctx->ct_srvaddr)
1724bff34e3Sthurlow 		printf(" srvaddr=\"%s\"\n", ctx->ct_srvaddr);
1734bff34e3Sthurlow 	else
1744bff34e3Sthurlow 		printf(" srvaddr=NULL\n");
1754bff34e3Sthurlow 
1764bff34e3Sthurlow 	dump_ctx_ssn(&ctx->ct_ssn);
1774bff34e3Sthurlow 	dump_ctx_sh(&ctx->ct_sh);
1784bff34e3Sthurlow }
1794bff34e3Sthurlow 
1804bff34e3Sthurlow /*
1814bff34e3Sthurlow  * Initialize an smb_ctx struct.
1824bff34e3Sthurlow  *
1834bff34e3Sthurlow  * The sequence for getting all the members filled in
1844bff34e3Sthurlow  * has some tricky aspects.  Here's how it works:
1854bff34e3Sthurlow  *
1864bff34e3Sthurlow  * The search order for options is as follows:
1874bff34e3Sthurlow  *   command line options
1884bff34e3Sthurlow  *   values parsed from UNC path (cmd)
1894bff34e3Sthurlow  *   values from RC file (per-user)
1904bff34e3Sthurlow  *   values from SMF (system-wide)
1914bff34e3Sthurlow  *   built-in defaults
1924bff34e3Sthurlow  *
1934bff34e3Sthurlow  * Normally, one would simply get all the values starting with
1944bff34e3Sthurlow  * the bottom of the above list and working to the top, and
1954bff34e3Sthurlow  * overwriting values as you go.  But we need an exception.
1964bff34e3Sthurlow  *
1974bff34e3Sthurlow  * In this function, we parse the UNC path and command line options,
1984bff34e3Sthurlow  * because we need (at least) the server name when we're getting the
1994bff34e3Sthurlow  * SMF and RC file values.  However, values we get from the command
2004bff34e3Sthurlow  * should not be overwritten by SMF or RC file parsing, so we mark
2014bff34e3Sthurlow  * values from the command as "from CMD" and the RC file parser
2024bff34e3Sthurlow  * leaves in place any values so marked.  See: SMBCF_CMD_*
2034bff34e3Sthurlow  *
2044bff34e3Sthurlow  * The semantics of these flags are: "This value came from the
2054bff34e3Sthurlow  * current command instance, not from sources that may apply to
2064bff34e3Sthurlow  * multiple commands."  (Different from the old "FROMUSR" flag.)
2074bff34e3Sthurlow  *
2084bff34e3Sthurlow  * Note that smb_ctx_opt() is called later to handle the
2094bff34e3Sthurlow  * remaining options, which should be ignored here.
2104bff34e3Sthurlow  * The (magic) leading ":" in cf_getopt() makes it
2114bff34e3Sthurlow  * ignore options not in the options string.
2124bff34e3Sthurlow  */
2134bff34e3Sthurlow int
2144bff34e3Sthurlow smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
2154bff34e3Sthurlow 	int minlevel, int maxlevel, int sharetype)
2164bff34e3Sthurlow {
2174bff34e3Sthurlow 	int  opt, error = 0;
2184bff34e3Sthurlow 	const char *arg, *cp;
2194bff34e3Sthurlow 	struct passwd pw;
2204bff34e3Sthurlow 	char pwbuf[NSS_BUFLEN_PASSWD];
2214bff34e3Sthurlow 	int aflg = 0, uflg = 0;
2224bff34e3Sthurlow 
2234bff34e3Sthurlow 	bzero(ctx, sizeof (*ctx));
2244bff34e3Sthurlow 	if (sharetype == SMB_ST_DISK)
2254bff34e3Sthurlow 		ctx->ct_flags |= SMBCF_BROWSEOK;
2264bff34e3Sthurlow 	error = nb_ctx_create(&ctx->ct_nb);
2274bff34e3Sthurlow 	if (error)
2284bff34e3Sthurlow 		return (error);
2294bff34e3Sthurlow 
2304bff34e3Sthurlow 	ctx->ct_fd = -1;
2314bff34e3Sthurlow 	ctx->ct_parsedlevel = SMBL_NONE;
2324bff34e3Sthurlow 	ctx->ct_minlevel = minlevel;
2334bff34e3Sthurlow 	ctx->ct_maxlevel = maxlevel;
2344bff34e3Sthurlow 
235*9c9af259SGordon Ross 	/* Fill in defaults */
2364bff34e3Sthurlow 	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE | SMBVOPT_MINAUTH_NTLM;
237*9c9af259SGordon Ross 
2384bff34e3Sthurlow 	ctx->ct_ssn.ioc_timeout = 15;
2394bff34e3Sthurlow 	ctx->ct_ssn.ioc_retrycount = 4;
2404bff34e3Sthurlow 	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
2414bff34e3Sthurlow 	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
2424bff34e3Sthurlow 	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
2434bff34e3Sthurlow 	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
2444bff34e3Sthurlow 
2454bff34e3Sthurlow 	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
2464bff34e3Sthurlow 	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
2474bff34e3Sthurlow 	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
2484bff34e3Sthurlow 	ctx->ct_sh.ioc_mode = SMBM_EXEC;
2494bff34e3Sthurlow 	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
2504bff34e3Sthurlow 	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
2514bff34e3Sthurlow 	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
2524bff34e3Sthurlow 
2534bff34e3Sthurlow 	nb_ctx_setscope(ctx->ct_nb, "");
2544bff34e3Sthurlow 
2554bff34e3Sthurlow 	/*
2564bff34e3Sthurlow 	 * if the user name is not specified some other way,
2574bff34e3Sthurlow 	 * use the current user name (built-in default)
2584bff34e3Sthurlow 	 */
2594bff34e3Sthurlow 	if (getpwuid_r(geteuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL)
2604bff34e3Sthurlow 		smb_ctx_setuser(ctx, pw.pw_name, 0);
2614bff34e3Sthurlow 
2624bff34e3Sthurlow 	/*
2634bff34e3Sthurlow 	 * Set a built-in default domain (workgroup).
2644bff34e3Sthurlow 	 * XXX: What's the best default? Use "?" instead?
2654bff34e3Sthurlow 	 * Using the Windows/NT default for now.
2664bff34e3Sthurlow 	 */
2674bff34e3Sthurlow 	smb_ctx_setworkgroup(ctx, "WORKGROUP", 0);
2684bff34e3Sthurlow 
2694bff34e3Sthurlow 	/*
2704bff34e3Sthurlow 	 * Parse the UNC path.  Values from here are
2714bff34e3Sthurlow 	 * marked as "from CMD".
2724bff34e3Sthurlow 	 */
2734bff34e3Sthurlow 	if (argv == NULL)
2744bff34e3Sthurlow 		goto done;
2754bff34e3Sthurlow 	for (opt = 1; opt < argc; opt++) {
2764bff34e3Sthurlow 		cp = argv[opt];
2774bff34e3Sthurlow 		if (strncmp(cp, "//", 2) != 0)
2784bff34e3Sthurlow 			continue;
2794bff34e3Sthurlow 		error = smb_ctx_parseunc(ctx, cp, sharetype, &cp);
2804bff34e3Sthurlow 		if (error)
2814bff34e3Sthurlow 			return (error);
2824bff34e3Sthurlow 		break;
2834bff34e3Sthurlow 	}
2844bff34e3Sthurlow 
2854bff34e3Sthurlow 	/*
2864bff34e3Sthurlow 	 * Parse options, if any.  Values from here too
2874bff34e3Sthurlow 	 * are marked as "from CMD".
2884bff34e3Sthurlow 	 */
2894bff34e3Sthurlow 	while (error == 0 && (opt = cf_getopt(argc, argv, ":AU:E:L:")) != -1) {
2904bff34e3Sthurlow 		arg = cf_optarg;
2914bff34e3Sthurlow 		switch (opt) {
2924bff34e3Sthurlow 		case 'A':
2934bff34e3Sthurlow 			aflg = 1;
2944bff34e3Sthurlow 			error = smb_ctx_setuser(ctx, "", TRUE);
2954bff34e3Sthurlow 			error = smb_ctx_setpassword(ctx, "", TRUE);
2964bff34e3Sthurlow 			ctx->ct_flags |= SMBCF_NOPWD;
2974bff34e3Sthurlow 			break;
2984bff34e3Sthurlow 		case 'E':
2994bff34e3Sthurlow #if 0 /* We don't support any "charset" stuff. (ignore -E) */
3004bff34e3Sthurlow 			error = smb_ctx_setcharset(ctx, arg);
3014bff34e3Sthurlow 			if (error)
3024bff34e3Sthurlow 				return (error);
3034bff34e3Sthurlow #endif
3044bff34e3Sthurlow 			break;
3054bff34e3Sthurlow 		case 'L':
3064bff34e3Sthurlow #if 0 /* Use the standard environment variables (ignore -L) */
3074bff34e3Sthurlow 			error = nls_setlocale(optarg);
3084bff34e3Sthurlow 			if (error)
3094bff34e3Sthurlow 				break;
3104bff34e3Sthurlow #endif
3114bff34e3Sthurlow 			break;
3124bff34e3Sthurlow 		case 'U':
3134bff34e3Sthurlow 			uflg = 1;
3144bff34e3Sthurlow 			error = smb_ctx_setuser(ctx, arg, TRUE);
3154bff34e3Sthurlow 			break;
3164bff34e3Sthurlow 		}
3174bff34e3Sthurlow 	}
3184bff34e3Sthurlow 	if (aflg && uflg)  {
3194bff34e3Sthurlow 		printf(gettext("-A and -U flags are exclusive.\n"));
3204bff34e3Sthurlow 		return (1);
3214bff34e3Sthurlow 	}
3224bff34e3Sthurlow 	cf_optind = cf_optreset = 1;
3234bff34e3Sthurlow 
3244bff34e3Sthurlow done:
3254bff34e3Sthurlow 	if (smb_debug)
3264bff34e3Sthurlow 		dump_ctx("after smb_ctx_init", ctx);
3274bff34e3Sthurlow 
3284bff34e3Sthurlow 	return (error);
3294bff34e3Sthurlow }
3304bff34e3Sthurlow 
3314bff34e3Sthurlow void
3324bff34e3Sthurlow smb_ctx_done(struct smb_ctx *ctx)
3334bff34e3Sthurlow {
3344bff34e3Sthurlow 
3354bff34e3Sthurlow 	rpc_cleanup_smbctx(ctx);
3364bff34e3Sthurlow 
3374bff34e3Sthurlow 	/* Kerberos stuff.  See smb_ctx_krb5init() */
3384bff34e3Sthurlow 	if (ctx->ct_krb5ctx) {
3394bff34e3Sthurlow 		if (ctx->ct_krb5cp)
3404bff34e3Sthurlow 			krb5_free_principal(ctx->ct_krb5ctx, ctx->ct_krb5cp);
3414bff34e3Sthurlow 		krb5_free_context(ctx->ct_krb5ctx);
3424bff34e3Sthurlow 	}
3434bff34e3Sthurlow 
3444bff34e3Sthurlow 	if (ctx->ct_fd != -1)
3454bff34e3Sthurlow 		close(ctx->ct_fd);
3464bff34e3Sthurlow #if 0 /* XXX: not pointers anymore */
3474bff34e3Sthurlow 	if (&ctx->ct_ssn.ioc_server)
3484bff34e3Sthurlow 		nb_snbfree(&ctx->ct_ssn.ioc_server);
3494bff34e3Sthurlow 	if (&ctx->ct_ssn.ioc_local)
3504bff34e3Sthurlow 		nb_snbfree(&ctx->ct_ssn.ioc_local);
3514bff34e3Sthurlow #endif
3524bff34e3Sthurlow 	if (ctx->ct_srvaddr)
3534bff34e3Sthurlow 		free(ctx->ct_srvaddr);
3544bff34e3Sthurlow 	if (ctx->ct_nb)
3554bff34e3Sthurlow 		nb_ctx_done(ctx->ct_nb);
3564bff34e3Sthurlow 	if (ctx->ct_secblob)
3574bff34e3Sthurlow 		free(ctx->ct_secblob);
3584bff34e3Sthurlow 	if (ctx->ct_origshare)
3594bff34e3Sthurlow 		free(ctx->ct_origshare);
3604bff34e3Sthurlow 	if (ctx->ct_fullserver)
3614bff34e3Sthurlow 		free(ctx->ct_fullserver);
3624bff34e3Sthurlow }
3634bff34e3Sthurlow 
3644bff34e3Sthurlow static int
3654bff34e3Sthurlow getsubstring(const char *p, uchar_t sep, char *dest, int maxlen,
3664bff34e3Sthurlow     const char **next)
3674bff34e3Sthurlow {
3684bff34e3Sthurlow 	int len;
3694bff34e3Sthurlow 
3704bff34e3Sthurlow 	maxlen--;
3714bff34e3Sthurlow 	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
3724bff34e3Sthurlow 		if (*p == 0)
3734bff34e3Sthurlow 			return (EINVAL);
3744bff34e3Sthurlow 		*dest = *p;
3754bff34e3Sthurlow 	}
3764bff34e3Sthurlow 	*dest = 0;
3774bff34e3Sthurlow 	*next = *p ? p + 1 : p;
3784bff34e3Sthurlow 	return (0);
3794bff34e3Sthurlow }
3804bff34e3Sthurlow 
3814bff34e3Sthurlow /*
3824bff34e3Sthurlow  * Parse the UNC path.  Here we expect something like
3834bff34e3Sthurlow  *   "//[workgroup;][user[:password]@]host[/share[/path]]"
3844bff34e3Sthurlow  * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
3854bff34e3Sthurlow  * Values found here are marked as "from CMD".
3864bff34e3Sthurlow  */
3874bff34e3Sthurlow int
3884bff34e3Sthurlow smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
3894bff34e3Sthurlow 	const char **next)
3904bff34e3Sthurlow {
3914bff34e3Sthurlow 	const char *p = unc;
3924bff34e3Sthurlow 	char *p1, *colon, *servername;
3934bff34e3Sthurlow 	char tmp[1024];
3944bff34e3Sthurlow 	char tmp2[1024];
3954bff34e3Sthurlow 	int error;
3964bff34e3Sthurlow 
3974bff34e3Sthurlow 	ctx->ct_parsedlevel = SMBL_NONE;
3984bff34e3Sthurlow 	if (*p++ != '/' || *p++ != '/') {
3994bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
4004bff34e3Sthurlow 		    "UNC should start with '//'"), 0);
4014bff34e3Sthurlow 		return (EINVAL);
4024bff34e3Sthurlow 	}
4034bff34e3Sthurlow 	p1 = tmp;
4044bff34e3Sthurlow 	error = getsubstring(p, ';', p1, sizeof (tmp), &p);
4054bff34e3Sthurlow 	if (!error) {
4064bff34e3Sthurlow 		if (*p1 == 0) {
4074bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
4084bff34e3Sthurlow 			    "empty workgroup name"), 0);
4094bff34e3Sthurlow 			return (EINVAL);
4104bff34e3Sthurlow 		}
4114bff34e3Sthurlow 		nls_str_upper(tmp, tmp);
4124bff34e3Sthurlow 		error = smb_ctx_setworkgroup(ctx, unpercent(tmp), TRUE);
4134bff34e3Sthurlow 		if (error)
4144bff34e3Sthurlow 			return (error);
4154bff34e3Sthurlow 	}
4164bff34e3Sthurlow 	colon = (char *)p;
4174bff34e3Sthurlow 	error = getsubstring(p, '@', p1, sizeof (tmp), &p);
4184bff34e3Sthurlow 	if (!error) {
4194bff34e3Sthurlow 		if (ctx->ct_maxlevel < SMBL_VC) {
4204bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
4214bff34e3Sthurlow 			    "no user name required"), 0);
4224bff34e3Sthurlow 			return (EINVAL);
4234bff34e3Sthurlow 		}
4244bff34e3Sthurlow 		p1 = strchr(tmp, ':');
4254bff34e3Sthurlow 		if (p1) {
4264bff34e3Sthurlow 			colon += p1 - tmp;
4274bff34e3Sthurlow 			*p1++ = (char)0;
4284bff34e3Sthurlow 			error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE);
4294bff34e3Sthurlow 			if (error)
4304bff34e3Sthurlow 				return (error);
4314bff34e3Sthurlow 			if (p - colon > 2)
4324bff34e3Sthurlow 				memset(colon+1, '*', p - colon - 2);
4334bff34e3Sthurlow 		}
4344bff34e3Sthurlow 		p1 = tmp;
4354bff34e3Sthurlow 		if (*p1 == 0) {
4364bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
4374bff34e3Sthurlow 			    "empty user name"), 0);
4384bff34e3Sthurlow 			return (EINVAL);
4394bff34e3Sthurlow 		}
4404bff34e3Sthurlow 		error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE);
4414bff34e3Sthurlow 		if (error)
4424bff34e3Sthurlow 			return (error);
4434bff34e3Sthurlow 		ctx->ct_parsedlevel = SMBL_VC;
4444bff34e3Sthurlow 	}
4454bff34e3Sthurlow 	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
4464bff34e3Sthurlow 	if (error) {
4474bff34e3Sthurlow 		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
4484bff34e3Sthurlow 		if (error) {
4494bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
4504bff34e3Sthurlow 			    "no server name found"), 0);
4514bff34e3Sthurlow 			return (error);
4524bff34e3Sthurlow 		}
4534bff34e3Sthurlow 	}
4544bff34e3Sthurlow 	if (*p1 == 0) {
4554bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
4564bff34e3Sthurlow 		return (EINVAL);
4574bff34e3Sthurlow 	}
4584bff34e3Sthurlow 
4594bff34e3Sthurlow 
4604bff34e3Sthurlow 	/*
4614bff34e3Sthurlow 	 * It's safe to uppercase this string, which
4624bff34e3Sthurlow 	 * consists of ascii characters that should
4634bff34e3Sthurlow 	 * be uppercased, %s, and ascii characters representing
4644bff34e3Sthurlow 	 * hex digits 0-9 and A-F (already uppercased, and
4654bff34e3Sthurlow 	 * if not uppercased they need to be). However,
4664bff34e3Sthurlow 	 * it is NOT safe to uppercase after it has been
4674bff34e3Sthurlow 	 * converted, below!
4684bff34e3Sthurlow 	 */
4694bff34e3Sthurlow 
4704bff34e3Sthurlow 	nls_str_upper(tmp2, tmp);
4714bff34e3Sthurlow 
4724bff34e3Sthurlow 	/*
4734bff34e3Sthurlow 	 * scan for % in the string.
4744bff34e3Sthurlow 	 * If we find one, convert
4754bff34e3Sthurlow 	 * to the assumed codepage.
4764bff34e3Sthurlow 	 */
4774bff34e3Sthurlow 
4784bff34e3Sthurlow 	if (strchr(tmp2, '%')) {
4794bff34e3Sthurlow 		/* use the 1st buffer, we don't need the old string */
4804bff34e3Sthurlow 		servername = tmp;
4814bff34e3Sthurlow 		if (!(servername = convert_utf8_to_wincs(unpercent(tmp2)))) {
4824bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN, "bad server name"), 0);
4834bff34e3Sthurlow 			return (EINVAL);
4844bff34e3Sthurlow 		}
4854bff34e3Sthurlow 		/*
4864bff34e3Sthurlow 		 * Converts utf8 to win equivalent of
4874bff34e3Sthurlow 		 * what is configured on this machine.
4884bff34e3Sthurlow 		 * Note that we are assuming this is the
4894bff34e3Sthurlow 		 * encoding used on the server, and that
4904bff34e3Sthurlow 		 * assumption might be incorrect. This is
4914bff34e3Sthurlow 		 * the best we can do now, and we should
4924bff34e3Sthurlow 		 * move to use port 445 to avoid having
4934bff34e3Sthurlow 		 * to worry about server codepages.
4944bff34e3Sthurlow 		 */
4954bff34e3Sthurlow 	} else /* no conversion needed */
4964bff34e3Sthurlow 		servername = tmp2;
4974bff34e3Sthurlow 
4984bff34e3Sthurlow 	smb_ctx_setserver(ctx, servername);
4994bff34e3Sthurlow 	error = smb_ctx_setfullserver(ctx, servername);
5004bff34e3Sthurlow 
5014bff34e3Sthurlow 	if (error)
5024bff34e3Sthurlow 		return (error);
5034bff34e3Sthurlow 	if (sharetype == SMB_ST_NONE) {
5044bff34e3Sthurlow 		*next = p;
5054bff34e3Sthurlow 		return (0);
5064bff34e3Sthurlow 	}
5074bff34e3Sthurlow 	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
5084bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0);
5094bff34e3Sthurlow 		return (EINVAL);
5104bff34e3Sthurlow 	}
5114bff34e3Sthurlow 	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
5124bff34e3Sthurlow 	if (error) {
5134bff34e3Sthurlow 		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
5144bff34e3Sthurlow 		if (error) {
5154bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
5164bff34e3Sthurlow 			    "unexpected end of line"), 0);
5174bff34e3Sthurlow 			return (error);
5184bff34e3Sthurlow 		}
5194bff34e3Sthurlow 	}
5204bff34e3Sthurlow 	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE &&
5214bff34e3Sthurlow 	    !(ctx->ct_flags & SMBCF_BROWSEOK)) {
5224bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
5234bff34e3Sthurlow 		return (EINVAL);
5244bff34e3Sthurlow 	}
5254bff34e3Sthurlow 	*next = p;
5264bff34e3Sthurlow 	if (*p1 == 0)
5274bff34e3Sthurlow 		return (0);
5284bff34e3Sthurlow 	error = smb_ctx_setshare(ctx, unpercent(p1), sharetype);
5294bff34e3Sthurlow 	return (error);
5304bff34e3Sthurlow }
5314bff34e3Sthurlow 
5324bff34e3Sthurlow int
5334bff34e3Sthurlow smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
5344bff34e3Sthurlow {
5354bff34e3Sthurlow 	char *cp, *servercs, *localcs;
5364bff34e3Sthurlow 	int cslen = sizeof (ctx->ct_ssn.ioc_localcs);
5374bff34e3Sthurlow 	int scslen, lcslen, error;
5384bff34e3Sthurlow 
5394bff34e3Sthurlow 	cp = strchr(arg, ':');
5404bff34e3Sthurlow 	lcslen = cp ? (cp - arg) : 0;
5414bff34e3Sthurlow 	if (lcslen == 0 || lcslen >= cslen) {
5424bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
5434bff34e3Sthurlow 		    "invalid local charset specification (%s)"), 0, arg);
5444bff34e3Sthurlow 		return (EINVAL);
5454bff34e3Sthurlow 	}
5464bff34e3Sthurlow 	scslen = (size_t)strlen(++cp);
5474bff34e3Sthurlow 	if (scslen == 0 || scslen >= cslen) {
5484bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
5494bff34e3Sthurlow 		    "invalid server charset specification (%s)"), 0, arg);
5504bff34e3Sthurlow 		return (EINVAL);
5514bff34e3Sthurlow 	}
5524bff34e3Sthurlow 	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
5534bff34e3Sthurlow 	localcs[lcslen] = 0;
5544bff34e3Sthurlow 	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
5554bff34e3Sthurlow 	error = nls_setrecode(localcs, servercs);
5564bff34e3Sthurlow 	if (error == 0)
5574bff34e3Sthurlow 		return (0);
5584bff34e3Sthurlow 	smb_error(dgettext(TEXT_DOMAIN,
5594bff34e3Sthurlow 	    "can't initialize iconv support (%s:%s)"),
5604bff34e3Sthurlow 	    error, localcs, servercs);
5614bff34e3Sthurlow 	localcs[0] = 0;
5624bff34e3Sthurlow 	servercs[0] = 0;
5634bff34e3Sthurlow 	return (error);
5644bff34e3Sthurlow }
5654bff34e3Sthurlow 
5664bff34e3Sthurlow int
5674bff34e3Sthurlow smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
5684bff34e3Sthurlow {
5694bff34e3Sthurlow 	ctx->ct_fullserver = strdup(name);
5704bff34e3Sthurlow 	if (ctx->ct_fullserver == NULL)
5714bff34e3Sthurlow 		return (ENOMEM);
5724bff34e3Sthurlow 	return (0);
5734bff34e3Sthurlow }
5744bff34e3Sthurlow 
5754bff34e3Sthurlow /*
5764bff34e3Sthurlow  * XXX TODO FIXME etc etc
5774bff34e3Sthurlow  * If the call to nbns_getnodestatus(...) fails we can try one of two other
5784bff34e3Sthurlow  * methods; use a name of "*SMBSERVER", which is supported by Samba (at least)
5794bff34e3Sthurlow  * or, as a last resort, try the "truncate-at-dot" heuristic.
5804bff34e3Sthurlow  * And the heuristic really should attempt truncation at
5814bff34e3Sthurlow  * each dot in turn, left to right.
5824bff34e3Sthurlow  *
5834bff34e3Sthurlow  * These fallback heuristics should be triggered when the attempt to open the
5844bff34e3Sthurlow  * session fails instead of in the code below.
5854bff34e3Sthurlow  *
5864bff34e3Sthurlow  * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
5874bff34e3Sthurlow  */
5884bff34e3Sthurlow int
5894bff34e3Sthurlow smb_ctx_getnbname(struct smb_ctx *ctx, struct sockaddr *sap)
5904bff34e3Sthurlow {
5914bff34e3Sthurlow 	char server[SMB_MAXSRVNAMELEN + 1];
5924bff34e3Sthurlow 	char workgroup[SMB_MAXUSERNAMELEN + 1];
5934bff34e3Sthurlow 	int error;
5944bff34e3Sthurlow #if 0
5954bff34e3Sthurlow 	char *dot;
5964bff34e3Sthurlow #endif
5974bff34e3Sthurlow 
5984bff34e3Sthurlow 	server[0] = workgroup[0] = '\0';
5994bff34e3Sthurlow 	error = nbns_getnodestatus(sap, ctx->ct_nb, server, workgroup);
6004bff34e3Sthurlow 	if (error == 0) {
6014bff34e3Sthurlow 		/*
6024bff34e3Sthurlow 		 * Used to set our domain name to be the same as
6034bff34e3Sthurlow 		 * the server's domain name.   Unnecessary at best,
6044bff34e3Sthurlow 		 * and wrong for accounts in a trusted domain.
6054bff34e3Sthurlow 		 */
6064bff34e3Sthurlow #ifdef APPLE
6074bff34e3Sthurlow 		if (workgroup[0] && !ctx->ct_ssn.ioc_workgroup[0])
6084bff34e3Sthurlow 			smb_ctx_setworkgroup(ctx, workgroup, 0);
6094bff34e3Sthurlow #endif
6104bff34e3Sthurlow 		if (server[0])
6114bff34e3Sthurlow 			smb_ctx_setserver(ctx, server);
6124bff34e3Sthurlow 	} else {
6134bff34e3Sthurlow 		if (smb_verbose)
6144bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
6154bff34e3Sthurlow 			    "Failed to get NetBIOS node status."), 0);
6164bff34e3Sthurlow 		if (ctx->ct_ssn.ioc_srvname[0] == (char)0)
6174bff34e3Sthurlow 			smb_ctx_setserver(ctx, "*SMBSERVER");
6184bff34e3Sthurlow 	}
6194bff34e3Sthurlow #if 0
6204bff34e3Sthurlow 	if (server[0] == (char)0) {
6214bff34e3Sthurlow 		dot = strchr(ctx->ct_fullserver, '.');
6224bff34e3Sthurlow 		if (dot)
6234bff34e3Sthurlow 			*dot = '\0';
6244bff34e3Sthurlow 		if (strlen(ctx->ct_fullserver) <= SMB_MAXSRVNAMELEN) {
6254bff34e3Sthurlow 			/*
6264bff34e3Sthurlow 			 * don't uppercase the server name. it comes from
6274bff34e3Sthurlow 			 * NBNS and uppercasing can clobber the characters
6284bff34e3Sthurlow 			 */
6294bff34e3Sthurlow 			strcpy(ctx->ct_ssn.ioc_srvname, ctx->ct_fullserver);
6304bff34e3Sthurlow 			error = 0;
6314bff34e3Sthurlow 		} else {
6324bff34e3Sthurlow 			error = -1;
6334bff34e3Sthurlow 		}
6344bff34e3Sthurlow 		if (dot)
6354bff34e3Sthurlow 			*dot = '.';
6364bff34e3Sthurlow 	}
6374bff34e3Sthurlow #endif
6384bff34e3Sthurlow 	return (error);
6394bff34e3Sthurlow }
6404bff34e3Sthurlow 
6414bff34e3Sthurlow /* this routine does not uppercase the server name */
6424bff34e3Sthurlow void
6434bff34e3Sthurlow smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
6444bff34e3Sthurlow {
6454bff34e3Sthurlow 	/* don't uppercase the server name */
6464bff34e3Sthurlow 	if (strlen(name) > SMB_MAXSRVNAMELEN) { /* NB limit is 15 */
6474bff34e3Sthurlow 		ctx->ct_ssn.ioc_srvname[0] = '\0';
6484bff34e3Sthurlow 	} else
6494bff34e3Sthurlow 		strcpy(ctx->ct_ssn.ioc_srvname, name);
6504bff34e3Sthurlow }
6514bff34e3Sthurlow 
6524bff34e3Sthurlow int
6534bff34e3Sthurlow smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
6544bff34e3Sthurlow {
6554bff34e3Sthurlow 
6564bff34e3Sthurlow 	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
6574bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
6584bff34e3Sthurlow 		    "user name '%s' too long"), 0, name);
6594bff34e3Sthurlow 		return (ENAMETOOLONG);
6604bff34e3Sthurlow 	}
6614bff34e3Sthurlow 
6624bff34e3Sthurlow 	/*
6634bff34e3Sthurlow 	 * Don't overwrite a value from the command line
6644bff34e3Sthurlow 	 * with one from anywhere else.
6654bff34e3Sthurlow 	 */
6664bff34e3Sthurlow 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR))
6674bff34e3Sthurlow 		return (0);
6684bff34e3Sthurlow 
6694bff34e3Sthurlow 	/* don't uppercase the username, just copy it. */
6704bff34e3Sthurlow 	strcpy(ctx->ct_ssn.ioc_user, name);
6714bff34e3Sthurlow 
6724bff34e3Sthurlow 	/* Mark this as "from the command line". */
6734bff34e3Sthurlow 	if (from_cmd)
6744bff34e3Sthurlow 		ctx->ct_flags |= SMBCF_CMD_USR;
6754bff34e3Sthurlow 
6764bff34e3Sthurlow 	return (0);
6774bff34e3Sthurlow }
6784bff34e3Sthurlow 
6794bff34e3Sthurlow /*
6804bff34e3Sthurlow  * Never uppercase the workgroup
6814bff34e3Sthurlow  * name here, because it might come
6824bff34e3Sthurlow  * from a Windows codepage encoding.
6834bff34e3Sthurlow  *
6844bff34e3Sthurlow  * Don't overwrite a domain name from the
6854bff34e3Sthurlow  * command line with one from anywhere else.
6864bff34e3Sthurlow  * See smb_ctx_init() for notes about this.
6874bff34e3Sthurlow  */
6884bff34e3Sthurlow int
6894bff34e3Sthurlow smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd)
6904bff34e3Sthurlow {
6914bff34e3Sthurlow 
6924bff34e3Sthurlow 	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
6934bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
6944bff34e3Sthurlow 		    "workgroup name '%s' too long"), 0, name);
6954bff34e3Sthurlow 		return (ENAMETOOLONG);
6964bff34e3Sthurlow 	}
6974bff34e3Sthurlow 
6984bff34e3Sthurlow 	/*
6994bff34e3Sthurlow 	 * Don't overwrite a value from the command line
7004bff34e3Sthurlow 	 * with one from anywhere else.
7014bff34e3Sthurlow 	 */
7024bff34e3Sthurlow 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
7034bff34e3Sthurlow 		return (0);
7044bff34e3Sthurlow 
7054bff34e3Sthurlow 	strcpy(ctx->ct_ssn.ioc_workgroup, name);
7064bff34e3Sthurlow 
7074bff34e3Sthurlow 	/* Mark this as "from the command line". */
7084bff34e3Sthurlow 	if (from_cmd)
7094bff34e3Sthurlow 		ctx->ct_flags |= SMBCF_CMD_DOM;
7104bff34e3Sthurlow 
7114bff34e3Sthurlow 	return (0);
7124bff34e3Sthurlow }
7134bff34e3Sthurlow 
7144bff34e3Sthurlow int
7154bff34e3Sthurlow smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
7164bff34e3Sthurlow {
7174bff34e3Sthurlow 
7184bff34e3Sthurlow 	if (passwd == NULL) /* XXX Huh? */
7194bff34e3Sthurlow 		return (EINVAL);
7204bff34e3Sthurlow 	if (strlen(passwd) >= SMB_MAXPASSWORDLEN) {
7214bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
7224bff34e3Sthurlow 		return (ENAMETOOLONG);
7234bff34e3Sthurlow 	}
7244bff34e3Sthurlow 
7254bff34e3Sthurlow 	/*
7264bff34e3Sthurlow 	 * Don't overwrite a value from the command line
7274bff34e3Sthurlow 	 * with one from anywhere else.
7284bff34e3Sthurlow 	 */
7294bff34e3Sthurlow 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW))
7304bff34e3Sthurlow 		return (0);
7314bff34e3Sthurlow 
7324bff34e3Sthurlow 	if (strncmp(passwd, "$$1", 3) == 0)
7334bff34e3Sthurlow 		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
7344bff34e3Sthurlow 	else
7354bff34e3Sthurlow 		strcpy(ctx->ct_ssn.ioc_password, passwd);
7364bff34e3Sthurlow 	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
7374bff34e3Sthurlow 
7384bff34e3Sthurlow 	/* Mark this as "from the command line". */
7394bff34e3Sthurlow 	if (from_cmd)
7404bff34e3Sthurlow 		ctx->ct_flags |= SMBCF_CMD_PW;
7414bff34e3Sthurlow 
7424bff34e3Sthurlow 	return (0);
7434bff34e3Sthurlow }
7444bff34e3Sthurlow 
7454bff34e3Sthurlow int
7464bff34e3Sthurlow smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
7474bff34e3Sthurlow {
7484bff34e3Sthurlow 	if (strlen(share) >= SMB_MAXSHARENAMELEN) {
7494bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
7504bff34e3Sthurlow 		    "share name '%s' too long"), 0, share);
7514bff34e3Sthurlow 		return (ENAMETOOLONG);
7524bff34e3Sthurlow 	}
7534bff34e3Sthurlow 	if (ctx->ct_origshare)
7544bff34e3Sthurlow 		free(ctx->ct_origshare);
7554bff34e3Sthurlow 	if ((ctx->ct_origshare = strdup(share)) == NULL)
7564bff34e3Sthurlow 		return (ENOMEM);
7574bff34e3Sthurlow 	nls_str_upper(ctx->ct_sh.ioc_share, share);
7584bff34e3Sthurlow 	if (share[0] != 0)
7594bff34e3Sthurlow 		ctx->ct_parsedlevel = SMBL_SHARE;
7604bff34e3Sthurlow 	ctx->ct_sh.ioc_stype = stype;
7614bff34e3Sthurlow 	return (0);
7624bff34e3Sthurlow }
7634bff34e3Sthurlow 
7644bff34e3Sthurlow int
7654bff34e3Sthurlow smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
7664bff34e3Sthurlow {
7674bff34e3Sthurlow 	if (addr == NULL || addr[0] == 0)
7684bff34e3Sthurlow 		return (EINVAL);
7694bff34e3Sthurlow 	if (ctx->ct_srvaddr)
7704bff34e3Sthurlow 		free(ctx->ct_srvaddr);
7714bff34e3Sthurlow 	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
7724bff34e3Sthurlow 		return (ENOMEM);
7734bff34e3Sthurlow 	return (0);
7744bff34e3Sthurlow }
7754bff34e3Sthurlow 
7764bff34e3Sthurlow static int
7774bff34e3Sthurlow smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
7784bff34e3Sthurlow {
7794bff34e3Sthurlow 	struct group gr;
7804bff34e3Sthurlow 	struct passwd pw;
7814bff34e3Sthurlow 	char buf[NSS_BUFLEN_PASSWD];
7824bff34e3Sthurlow 	char *cp;
7834bff34e3Sthurlow 
7844bff34e3Sthurlow 	cp = strchr(pair, ':');
7854bff34e3Sthurlow 	if (cp) {
7864bff34e3Sthurlow 		*cp++ = '\0';
7874bff34e3Sthurlow 		if (*cp) {
7884bff34e3Sthurlow 			if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) {
7894bff34e3Sthurlow 				*gid = gr.gr_gid;
7904bff34e3Sthurlow 			} else
7914bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
7924bff34e3Sthurlow 				    "Invalid group name %s, ignored"), 0, cp);
7934bff34e3Sthurlow 		}
7944bff34e3Sthurlow 	}
7954bff34e3Sthurlow 	if (*pair) {
7964bff34e3Sthurlow 		if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) {
7974bff34e3Sthurlow 			*uid = pw.pw_uid;
7984bff34e3Sthurlow 		} else
7994bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
8004bff34e3Sthurlow 			    "Invalid user name %s, ignored"), 0, pair);
8014bff34e3Sthurlow 	}
8024bff34e3Sthurlow 
8034bff34e3Sthurlow 	return (0);
8044bff34e3Sthurlow }
8054bff34e3Sthurlow 
8064bff34e3Sthurlow /*
8074bff34e3Sthurlow  * Commands use this with getopt.  See:
8084bff34e3Sthurlow  *   STDPARAM_OPT, STDPARAM_ARGS
8094bff34e3Sthurlow  * Called after smb_ctx_readrc().
8104bff34e3Sthurlow  */
8114bff34e3Sthurlow int
8124bff34e3Sthurlow smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
8134bff34e3Sthurlow {
8144bff34e3Sthurlow 	int error = 0;
8154bff34e3Sthurlow 	char *p, *cp;
8164bff34e3Sthurlow 	char tmp[1024];
8174bff34e3Sthurlow 
8184bff34e3Sthurlow 	switch (opt) {
8194bff34e3Sthurlow 	case 'A':
8204bff34e3Sthurlow 	case 'U':
8214bff34e3Sthurlow 		/* Handled in smb_ctx_init() */
8224bff34e3Sthurlow 		break;
8234bff34e3Sthurlow 	case 'I':
8244bff34e3Sthurlow 		error = smb_ctx_setsrvaddr(ctx, arg);
8254bff34e3Sthurlow 		break;
8264bff34e3Sthurlow 	case 'M':
8274bff34e3Sthurlow 		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
8284bff34e3Sthurlow 		if (*cp == '/') {
8294bff34e3Sthurlow 			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
8304bff34e3Sthurlow 			ctx->ct_flags |= SMBCF_SRIGHTS;
8314bff34e3Sthurlow 		}
8324bff34e3Sthurlow 		break;
8334bff34e3Sthurlow 	case 'N':
8344bff34e3Sthurlow 		ctx->ct_flags |= SMBCF_NOPWD;
8354bff34e3Sthurlow 		break;
8364bff34e3Sthurlow 	case 'O':
8374bff34e3Sthurlow 		p = strdup(arg);
8384bff34e3Sthurlow 		cp = strchr(p, '/');
8394bff34e3Sthurlow 		if (cp) {
8404bff34e3Sthurlow 			*cp++ = '\0';
8414bff34e3Sthurlow 			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
8424bff34e3Sthurlow 			    &ctx->ct_sh.ioc_group);
8434bff34e3Sthurlow 		}
8444bff34e3Sthurlow 		if (*p && error == 0) {
8454bff34e3Sthurlow 			error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner,
8464bff34e3Sthurlow 			    &ctx->ct_ssn.ioc_group);
8474bff34e3Sthurlow 		}
8484bff34e3Sthurlow 		free(p);
8494bff34e3Sthurlow 		break;
8504bff34e3Sthurlow 	case 'P':
8514bff34e3Sthurlow /*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT; */
8524bff34e3Sthurlow 		break;
8534bff34e3Sthurlow 	case 'R':
8544bff34e3Sthurlow 		ctx->ct_ssn.ioc_retrycount = atoi(arg);
8554bff34e3Sthurlow 		break;
8564bff34e3Sthurlow 	case 'T':
8574bff34e3Sthurlow 		ctx->ct_ssn.ioc_timeout = atoi(arg);
8584bff34e3Sthurlow 		break;
8594bff34e3Sthurlow 	case 'W':
8604bff34e3Sthurlow 		nls_str_upper(tmp, arg);
8614bff34e3Sthurlow 		error = smb_ctx_setworkgroup(ctx, tmp, TRUE);
8624bff34e3Sthurlow 		break;
8634bff34e3Sthurlow 	}
8644bff34e3Sthurlow 	return (error);
8654bff34e3Sthurlow }
8664bff34e3Sthurlow 
8674bff34e3Sthurlow #if 0
8684bff34e3Sthurlow static void
8694bff34e3Sthurlow smb_hexdump(const uchar_t *buf, int len) {
8704bff34e3Sthurlow 	int ofs = 0;
8714bff34e3Sthurlow 
8724bff34e3Sthurlow 	while (len--) {
8734bff34e3Sthurlow 		if (ofs % 16 == 0)
8744bff34e3Sthurlow 			printf("\n%02X: ", ofs);
8754bff34e3Sthurlow 		printf("%02x ", *buf++);
8764bff34e3Sthurlow 		ofs++;
8774bff34e3Sthurlow 	}
8784bff34e3Sthurlow 	printf("\n");
8794bff34e3Sthurlow }
8804bff34e3Sthurlow #endif
8814bff34e3Sthurlow 
8824bff34e3Sthurlow 
8834bff34e3Sthurlow static int
8844bff34e3Sthurlow smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl)
8854bff34e3Sthurlow {
8864bff34e3Sthurlow 	int error;
8874bff34e3Sthurlow 
8884bff34e3Sthurlow 	/*
8894bff34e3Sthurlow 	 * Not able to find out what is the work of this routine till
8904bff34e3Sthurlow 	 * now. Still investigating.
8914bff34e3Sthurlow 	 * REVISIT
8924bff34e3Sthurlow 	 */
8934bff34e3Sthurlow #ifdef KICONV_SUPPORT
8944bff34e3Sthurlow 	error = kiconv_add_xlat_table(to, from, tbl);
8954bff34e3Sthurlow 	if (error && error != EEXIST) {
8964bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
8974bff34e3Sthurlow 		    "can not setup kernel iconv table (%s:%s)"),
8984bff34e3Sthurlow 		    error, from, to);
8994bff34e3Sthurlow 		return (error);
9004bff34e3Sthurlow 	}
9014bff34e3Sthurlow #endif
9024bff34e3Sthurlow 	return (0);
9034bff34e3Sthurlow }
9044bff34e3Sthurlow 
9054bff34e3Sthurlow /*
9064bff34e3Sthurlow  * Verify context before connect operation(s),
9074bff34e3Sthurlow  * lookup specified server and try to fill all forgotten fields.
9084bff34e3Sthurlow  */
9094bff34e3Sthurlow int
9104bff34e3Sthurlow smb_ctx_resolve(struct smb_ctx *ctx)
9114bff34e3Sthurlow {
9124bff34e3Sthurlow 	struct smbioc_ossn *ssn = &ctx->ct_ssn;
9134bff34e3Sthurlow 	struct smbioc_oshare *sh = &ctx->ct_sh;
9144bff34e3Sthurlow 	struct nb_name nn;
9154bff34e3Sthurlow 	struct sockaddr *sap;
9164bff34e3Sthurlow 	struct sockaddr_nb *salocal, *saserver;
9174bff34e3Sthurlow 	char *cp;
9184bff34e3Sthurlow 	uchar_t cstbl[256];
9194bff34e3Sthurlow 	uint_t i;
9204bff34e3Sthurlow 	int error = 0;
9214bff34e3Sthurlow 	int browseok = ctx->ct_flags & SMBCF_BROWSEOK;
9224bff34e3Sthurlow 	int renego = 0;
9234bff34e3Sthurlow 
9244bff34e3Sthurlow 	ctx->ct_flags &= ~SMBCF_RESOLVED;
9254bff34e3Sthurlow 	if (isatty(STDIN_FILENO))
9264bff34e3Sthurlow 		browseok = 0;
9274bff34e3Sthurlow 	if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == 0) {
9284bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
9294bff34e3Sthurlow 		    "no server name specified"), 0);
9304bff34e3Sthurlow 		return (EINVAL);
9314bff34e3Sthurlow 	}
9324bff34e3Sthurlow 	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0 &&
9334bff34e3Sthurlow 	    !browseok) {
9344bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
9354bff34e3Sthurlow 		    "no share name specified for %s@%s"),
9364bff34e3Sthurlow 		    0, ssn->ioc_user, ssn->ioc_srvname);
9374bff34e3Sthurlow 		return (EINVAL);
9384bff34e3Sthurlow 	}
9394bff34e3Sthurlow 	error = nb_ctx_resolve(ctx->ct_nb);
9404bff34e3Sthurlow 	if (error)
9414bff34e3Sthurlow 		return (error);
9424bff34e3Sthurlow 	if (ssn->ioc_localcs[0] == 0)
9434bff34e3Sthurlow 		strcpy(ssn->ioc_localcs, "default");	/* XXX: locale name ? */
9444bff34e3Sthurlow 	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
9454bff34e3Sthurlow 	if (error)
9464bff34e3Sthurlow 		return (error);
9474bff34e3Sthurlow 	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
9484bff34e3Sthurlow 	if (error)
9494bff34e3Sthurlow 		return (error);
9504bff34e3Sthurlow 	if (ssn->ioc_servercs[0] != 0) {
9514bff34e3Sthurlow 		for (i = 0; i < sizeof (cstbl); i++)
9524bff34e3Sthurlow 			cstbl[i] = i;
9534bff34e3Sthurlow 		nls_mem_toext(cstbl, cstbl, sizeof (cstbl));
9544bff34e3Sthurlow 		error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs,
9554bff34e3Sthurlow 		    cstbl);
9564bff34e3Sthurlow 		if (error)
9574bff34e3Sthurlow 			return (error);
9584bff34e3Sthurlow 		for (i = 0; i < sizeof (cstbl); i++)
9594bff34e3Sthurlow 			cstbl[i] = i;
9604bff34e3Sthurlow 		nls_mem_toloc(cstbl, cstbl, sizeof (cstbl));
9614bff34e3Sthurlow 		error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs,
9624bff34e3Sthurlow 		    cstbl);
9634bff34e3Sthurlow 		if (error)
9644bff34e3Sthurlow 			return (error);
9654bff34e3Sthurlow 	}
9664bff34e3Sthurlow 	/*
9674bff34e3Sthurlow 	 * If we have an explicit address set for the server in
9684bff34e3Sthurlow 	 * an "addr=X" setting in .nsmbrc or SMF, just try using a
9694bff34e3Sthurlow 	 * gethostbyname() lookup for it.
9704bff34e3Sthurlow 	 */
9714bff34e3Sthurlow 	if (ctx->ct_srvaddr) {
9724bff34e3Sthurlow 		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap);
9734bff34e3Sthurlow 		if (error == 0)
9744bff34e3Sthurlow 			(void) smb_ctx_getnbname(ctx, sap);
9754bff34e3Sthurlow 	} else
9764bff34e3Sthurlow 		error = -1;
9774bff34e3Sthurlow 
9784bff34e3Sthurlow 	/*
9794bff34e3Sthurlow 	 * Next try a gethostbyname() lookup on the original user-
9804bff34e3Sthurlow 	 * specified server name. This is similar to Windows
9814bff34e3Sthurlow 	 * NBT option "Use DNS for name resolution."
9824bff34e3Sthurlow 	 */
9834bff34e3Sthurlow 	if (error && ctx->ct_fullserver) {
9844bff34e3Sthurlow 		error = nb_resolvehost_in(ctx->ct_fullserver, &sap);
9854bff34e3Sthurlow 		if (error == 0)
9864bff34e3Sthurlow 			(void) smb_ctx_getnbname(ctx, sap);
9874bff34e3Sthurlow 	}
9884bff34e3Sthurlow 
9894bff34e3Sthurlow 	/*
9904bff34e3Sthurlow 	 * Finally, try the shorter, upper-cased ssn->ioc_srvname
9914bff34e3Sthurlow 	 * with a NBNS/WINS lookup if the "nbns_enable" property is
9924bff34e3Sthurlow 	 * true (the default).  nbns_resolvename() may unicast to the
9934bff34e3Sthurlow 	 * "nbns" server or broadcast on the subnet.
9944bff34e3Sthurlow 	 */
9954bff34e3Sthurlow 	if (error && ssn->ioc_srvname[0] &&
9964bff34e3Sthurlow 	    ctx->ct_nb->nb_flags & NBCF_NS_ENABLE) {
9974bff34e3Sthurlow 		error = nbns_resolvename(ssn->ioc_srvname,
9984bff34e3Sthurlow 		    ctx->ct_nb, &sap);
9994bff34e3Sthurlow 		/*
10004bff34e3Sthurlow 		 * Used to get the NetBIOS node status here.
10014bff34e3Sthurlow 		 * Not necessary (we have the NetBIOS name).
10024bff34e3Sthurlow 		 */
10034bff34e3Sthurlow 	}
10044bff34e3Sthurlow 	if (error) {
10054bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
10064bff34e3Sthurlow 		    "can't get server address"), error);
10074bff34e3Sthurlow 		return (error);
10084bff34e3Sthurlow 	}
10094bff34e3Sthurlow 
10104bff34e3Sthurlow 	/* XXX: no nls_str_upper(ssn->ioc_srvname) here? */
10114bff34e3Sthurlow 
10124bff34e3Sthurlow 	assert(sizeof (nn.nn_name) == sizeof (ssn->ioc_srvname));
10134bff34e3Sthurlow 	memcpy(nn.nn_name, ssn->ioc_srvname, NB_NAMELEN);
10144bff34e3Sthurlow 	nn.nn_type = NBT_SERVER;
10154bff34e3Sthurlow 	nn.nn_scope = ctx->ct_nb->nb_scope;
10164bff34e3Sthurlow 
10174bff34e3Sthurlow 	error = nb_sockaddr(sap, &nn, &saserver);
10184bff34e3Sthurlow 	memcpy(&ctx->ct_srvinaddr, sap, sizeof (struct sockaddr_in));
10194bff34e3Sthurlow 	nb_snbfree(sap);
10204bff34e3Sthurlow 	if (error) {
10214bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
10224bff34e3Sthurlow 		    "can't allocate server address"), error);
10234bff34e3Sthurlow 		return (error);
10244bff34e3Sthurlow 	}
10254bff34e3Sthurlow 	/* We know it's a NetBIOS address here. */
10264bff34e3Sthurlow 	bcopy(saserver, &ssn->ioc_server.nb,
10274bff34e3Sthurlow 	    sizeof (struct sockaddr_nb));
10284bff34e3Sthurlow 	if (ctx->ct_locname[0] == 0) {
10294bff34e3Sthurlow 		error = nb_getlocalname(ctx->ct_locname,
10304bff34e3Sthurlow 		    SMB_MAXUSERNAMELEN + 1);
10314bff34e3Sthurlow 		if (error) {
10324bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
10334bff34e3Sthurlow 			    "can't get local name"), error);
10344bff34e3Sthurlow 			return (error);
10354bff34e3Sthurlow 		}
10364bff34e3Sthurlow 		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
10374bff34e3Sthurlow 	}
10384bff34e3Sthurlow 
10394bff34e3Sthurlow 	/* XXX: no nls_str_upper(ctx->ct_locname); here? */
10404bff34e3Sthurlow 
10414bff34e3Sthurlow 	memcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN);
10424bff34e3Sthurlow 	nn.nn_type = NBT_WKSTA;
10434bff34e3Sthurlow 	nn.nn_scope = ctx->ct_nb->nb_scope;
10444bff34e3Sthurlow 
10454bff34e3Sthurlow 	error = nb_sockaddr(NULL, &nn, &salocal);
10464bff34e3Sthurlow 	if (error) {
10474bff34e3Sthurlow 		nb_snbfree((struct sockaddr *)saserver);
10484bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
10494bff34e3Sthurlow 		    "can't allocate local address"), error);
10504bff34e3Sthurlow 		return (error);
10514bff34e3Sthurlow 	}
10524bff34e3Sthurlow 
10534bff34e3Sthurlow 	/* We know it's a NetBIOS address here. */
10544bff34e3Sthurlow 	bcopy(salocal, &ssn->ioc_local.nb,
10554bff34e3Sthurlow 	    sizeof (struct sockaddr_nb));
10564bff34e3Sthurlow 
10571b34bc4aSbs135383 	error = smb_ctx_findvc(ctx, SMBL_VC, 0);
10581b34bc4aSbs135383 	if (error == 0) {
10591b34bc4aSbs135383 		/* re-use and existing VC */
10601b34bc4aSbs135383 		ctx->ct_flags |= SMBCF_RESOLVED | SMBCF_SSNACTIVE;
10611b34bc4aSbs135383 		return (0);
10621b34bc4aSbs135383 	}
10631b34bc4aSbs135383 
10641b34bc4aSbs135383 	/* Make a new connection via smb_ctx_negotiate()... */
10654bff34e3Sthurlow 	error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE,
10664bff34e3Sthurlow 	    ssn->ioc_workgroup);
10674bff34e3Sthurlow 	if (error)
10684bff34e3Sthurlow 		return (error);
10694bff34e3Sthurlow 	ctx->ct_flags &= ~SMBCF_AUTHREQ;
10704bff34e3Sthurlow 	if (!ctx->ct_secblob && browseok && !sh->ioc_share[0] &&
10714bff34e3Sthurlow 	    !(ctx->ct_flags & SMBCF_XXX)) {
10724bff34e3Sthurlow 		/* assert: anon share list is subset of overall server shares */
10734bff34e3Sthurlow 		error = smb_browse(ctx, 1);
10744bff34e3Sthurlow 		if (error) /* user cancel or other error? */
10754bff34e3Sthurlow 			return (error);
10764bff34e3Sthurlow 		/*
10774bff34e3Sthurlow 		 * A share was selected, authenticate button was pressed,
10784bff34e3Sthurlow 		 * or anon-authentication failed getting browse list.
10794bff34e3Sthurlow 		 */
10804bff34e3Sthurlow 	}
10814bff34e3Sthurlow 	if ((ctx->ct_secblob == NULL) && (ctx->ct_flags & SMBCF_AUTHREQ ||
10824bff34e3Sthurlow 	    (ssn->ioc_password[0] == '\0' &&
10834bff34e3Sthurlow 	    !(ctx->ct_flags & SMBCF_NOPWD)))) {
10844bff34e3Sthurlow reauth:
10854bff34e3Sthurlow 		/*
10864bff34e3Sthurlow 		 * This function is implemented in both
10874bff34e3Sthurlow 		 * ui-apple.c and ui-sun.c so let's try to
10884bff34e3Sthurlow 		 * keep the same interface.  Not sure why
10894bff34e3Sthurlow 		 * they didn't just pass ssn here.
10904bff34e3Sthurlow 		 */
10914bff34e3Sthurlow 		error = smb_get_authentication(
10924bff34e3Sthurlow 		    ssn->ioc_workgroup, sizeof (ssn->ioc_workgroup) - 1,
10934bff34e3Sthurlow 		    ssn->ioc_user, sizeof (ssn->ioc_user) - 1,
10944bff34e3Sthurlow 		    ssn->ioc_password, sizeof (ssn->ioc_password) - 1,
10954bff34e3Sthurlow 		    ssn->ioc_srvname, ctx);
10964bff34e3Sthurlow 		if (error)
10974bff34e3Sthurlow 			return (error);
10984bff34e3Sthurlow 	}
10994bff34e3Sthurlow 	/*
11004bff34e3Sthurlow 	 * if we have a session it is either anonymous
11014bff34e3Sthurlow 	 * or from a stale authentication.  re-negotiating
11024bff34e3Sthurlow 	 * gets us ready for a fresh session
11034bff34e3Sthurlow 	 */
11044bff34e3Sthurlow 	if (ctx->ct_flags & SMBCF_SSNACTIVE || renego) {
11054bff34e3Sthurlow 		renego = 0;
11064bff34e3Sthurlow 		/* don't clobber workgroup name, pass null arg */
11074bff34e3Sthurlow 		error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, NULL);
11084bff34e3Sthurlow 		if (error)
11094bff34e3Sthurlow 			return (error);
11104bff34e3Sthurlow 	}
11114bff34e3Sthurlow 	if (browseok && !sh->ioc_share[0]) {
11124bff34e3Sthurlow 		ctx->ct_flags &= ~SMBCF_AUTHREQ;
11134bff34e3Sthurlow 		error = smb_browse(ctx, 0);
11144bff34e3Sthurlow 		if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) {
11154bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
11164bff34e3Sthurlow 			    "smb_ctx_resolve: bad keychain entry"), 0);
11174bff34e3Sthurlow 			ctx->ct_flags |= SMBCF_KCBAD;
11184bff34e3Sthurlow 			renego = 1;
11194bff34e3Sthurlow 			goto reauth;
11204bff34e3Sthurlow 		}
11214bff34e3Sthurlow 		if (error) /* auth, user cancel, or other error */
11224bff34e3Sthurlow 			return (error);
11234bff34e3Sthurlow 		/*
11244bff34e3Sthurlow 		 * Re-authenticate button was pressed?
11254bff34e3Sthurlow 		 */
11264bff34e3Sthurlow 		if (ctx->ct_flags & SMBCF_AUTHREQ)
11274bff34e3Sthurlow 			goto reauth;
11284bff34e3Sthurlow 		if (!sh->ioc_share[0] && !(ctx->ct_flags & SMBCF_XXX)) {
11294bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
11304bff34e3Sthurlow 			    "no share specified for %s@%s"),
11314bff34e3Sthurlow 			    0, ssn->ioc_user, ssn->ioc_srvname);
11324bff34e3Sthurlow 			return (EINVAL);
11334bff34e3Sthurlow 		}
11344bff34e3Sthurlow 	}
11354bff34e3Sthurlow 	ctx->ct_flags |= SMBCF_RESOLVED;
11364bff34e3Sthurlow 
11374bff34e3Sthurlow 	if (smb_debug)
11384bff34e3Sthurlow 		dump_ctx("after smb_ctx_resolve", ctx);
11394bff34e3Sthurlow 
11404bff34e3Sthurlow 	return (0);
11414bff34e3Sthurlow }
11424bff34e3Sthurlow 
11434bff34e3Sthurlow int
11444bff34e3Sthurlow smb_open_driver()
11454bff34e3Sthurlow {
11464bff34e3Sthurlow 	char buf[20];
11474bff34e3Sthurlow 	int err, fd, i;
11484bff34e3Sthurlow 	uint32_t version;
11494bff34e3Sthurlow 
11504bff34e3Sthurlow 	/*
11514bff34e3Sthurlow 	 * First try to open as clone
11524bff34e3Sthurlow 	 */
11534bff34e3Sthurlow 	fd = open("/dev/"NSMB_NAME, O_RDWR);
11544bff34e3Sthurlow 	if (fd >= 0)
11554bff34e3Sthurlow 		goto opened;
11564bff34e3Sthurlow 
11574bff34e3Sthurlow 	err = errno; /* from open */
11584bff34e3Sthurlow #ifdef APPLE
11594bff34e3Sthurlow 	/*
11604bff34e3Sthurlow 	 * well, no clone capabilities available - we have to scan
11614bff34e3Sthurlow 	 * all devices in order to get free one
11624bff34e3Sthurlow 	 */
11634bff34e3Sthurlow 	for (i = 0; i < 1024; i++) {
11644bff34e3Sthurlow 		snprintf(buf, sizeof (buf), "/dev/%s%d", NSMB_NAME, i);
11654bff34e3Sthurlow 		fd = open(buf, O_RDWR);
11664bff34e3Sthurlow 		if (fd >= 0)
11674bff34e3Sthurlow 			goto opened;
11684bff34e3Sthurlow 		if (i && POWEROF2(i+1))
11694bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
11704bff34e3Sthurlow 			    "%d failures to open smb device"), errno, i+1);
11714bff34e3Sthurlow 	}
11724bff34e3Sthurlow 	err = ENOENT;
11734bff34e3Sthurlow #endif
11744bff34e3Sthurlow 	smb_error(dgettext(TEXT_DOMAIN,
11754bff34e3Sthurlow 	    "failed to open %s"), err, "/dev/" NSMB_NAME);
11764bff34e3Sthurlow 	return (-1);
11774bff34e3Sthurlow 
11784bff34e3Sthurlow opened:
11794bff34e3Sthurlow 	/*
11804bff34e3Sthurlow 	 * Check the driver version (paranoia)
11814bff34e3Sthurlow 	 * Do this BEFORE any other ioctl calls.
11824bff34e3Sthurlow 	 */
11834bff34e3Sthurlow 	if (ioctl(fd, SMBIOC_GETVERS, &version) < 0) {
11844bff34e3Sthurlow 		err = errno;
11854bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
11864bff34e3Sthurlow 		    "failed to get driver version"), err);
11874bff34e3Sthurlow 		close(fd);
11884bff34e3Sthurlow 		return (-1);
11894bff34e3Sthurlow 	}
11904bff34e3Sthurlow 	if (version != NSMB_VERSION) {
11914bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
11924bff34e3Sthurlow 		    "incorrect driver version"), 0);
11934bff34e3Sthurlow 		close(fd);
11944bff34e3Sthurlow 		return (-1);
11954bff34e3Sthurlow 	}
11964bff34e3Sthurlow 
11974bff34e3Sthurlow 	return (fd);
11984bff34e3Sthurlow }
11994bff34e3Sthurlow 
12004bff34e3Sthurlow static int
12014bff34e3Sthurlow smb_ctx_gethandle(struct smb_ctx *ctx)
12024bff34e3Sthurlow {
12034bff34e3Sthurlow 	int err, fd;
12044bff34e3Sthurlow 
12054bff34e3Sthurlow 	if (ctx->ct_fd != -1) {
12064bff34e3Sthurlow 		rpc_cleanup_smbctx(ctx);
12074bff34e3Sthurlow 		close(ctx->ct_fd);
12084bff34e3Sthurlow 		ctx->ct_fd = -1;
12094bff34e3Sthurlow 		ctx->ct_flags &= ~SMBCF_SSNACTIVE;
12104bff34e3Sthurlow 	}
12114bff34e3Sthurlow 
12124bff34e3Sthurlow 	fd = smb_open_driver();
12134bff34e3Sthurlow 	if (fd < 0)
12144bff34e3Sthurlow 		return (ENODEV);
12154bff34e3Sthurlow 
12164bff34e3Sthurlow 	ctx->ct_fd = fd;
12174bff34e3Sthurlow 	return (0);
12184bff34e3Sthurlow }
12194bff34e3Sthurlow 
12204bff34e3Sthurlow int
12214bff34e3Sthurlow smb_ctx_ioctl(struct smb_ctx *ctx, int inum, struct smbioc_lookup *rqp)
12224bff34e3Sthurlow {
12234bff34e3Sthurlow 	size_t	siz = DEF_SEC_TOKEN_LEN;
12244bff34e3Sthurlow 	int	rc = 0;
12254bff34e3Sthurlow 	struct sockaddr sap1, sap2;
12264bff34e3Sthurlow 	int i;
12274bff34e3Sthurlow 
12284bff34e3Sthurlow 	if (rqp->ioc_ssn.ioc_outtok)
12294bff34e3Sthurlow 		free(rqp->ioc_ssn.ioc_outtok);
12304bff34e3Sthurlow 	rqp->ioc_ssn.ioc_outtoklen = siz;
12314bff34e3Sthurlow 	rqp->ioc_ssn.ioc_outtok = malloc(siz+1);
12324bff34e3Sthurlow 	if (rqp->ioc_ssn.ioc_outtok == NULL)
12334bff34e3Sthurlow 		return (ENOMEM);
12344bff34e3Sthurlow 	bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
12354bff34e3Sthurlow 	/* Note: No longer put length in outtok[0] */
12364bff34e3Sthurlow 	/* *((int *)rqp->ioc_ssn.ioc_outtok) = (int)siz; */
12374bff34e3Sthurlow 
12384bff34e3Sthurlow 	if (ioctl(ctx->ct_fd, inum, rqp) == -1) {
12394bff34e3Sthurlow 		rc = errno;
12404bff34e3Sthurlow 		goto out;
12414bff34e3Sthurlow 	}
12424bff34e3Sthurlow 	if (rqp->ioc_ssn.ioc_outtoklen <= siz)
12434bff34e3Sthurlow 		goto out;
12444bff34e3Sthurlow 
12454bff34e3Sthurlow 	/*
12464bff34e3Sthurlow 	 * Operation completed, but our output token wasn't large enough.
12474bff34e3Sthurlow 	 * The re-call below only pulls the token from the kernel.
12484bff34e3Sthurlow 	 */
12494bff34e3Sthurlow 	siz = rqp->ioc_ssn.ioc_outtoklen;
12504bff34e3Sthurlow 	free(rqp->ioc_ssn.ioc_outtok);
12514bff34e3Sthurlow 	rqp->ioc_ssn.ioc_outtok = malloc(siz + 1);
12524bff34e3Sthurlow 	if (rqp->ioc_ssn.ioc_outtok == NULL) {
12534bff34e3Sthurlow 		rc = ENOMEM;
12544bff34e3Sthurlow 		goto out;
12554bff34e3Sthurlow 	}
12564bff34e3Sthurlow 	bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
12574bff34e3Sthurlow 	/* Note: No longer put length in outtok[0] */
12584bff34e3Sthurlow 	/* *((int *)rqp->ioc_ssn.ioc_outtok) = siz; */
12594bff34e3Sthurlow 	if (ioctl(ctx->ct_fd, inum, rqp) == -1)
12604bff34e3Sthurlow 		rc = errno;
12614bff34e3Sthurlow out:
12624bff34e3Sthurlow 	return (rc);
12634bff34e3Sthurlow }
12644bff34e3Sthurlow 
12651b34bc4aSbs135383 int
12661b34bc4aSbs135383 smb_ctx_findvc(struct smb_ctx *ctx, int level, int flags)
12671b34bc4aSbs135383 {
12681b34bc4aSbs135383 	struct smbioc_lookup	rq;
12691b34bc4aSbs135383 	int	error = 0;
12701b34bc4aSbs135383 
12711b34bc4aSbs135383 	if ((error = smb_ctx_gethandle(ctx)))
12721b34bc4aSbs135383 		return (error);
12731b34bc4aSbs135383 
12741b34bc4aSbs135383 	bzero(&rq, sizeof (rq));
12751b34bc4aSbs135383 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
12761b34bc4aSbs135383 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
12771b34bc4aSbs135383 
12781b34bc4aSbs135383 	rq.ioc_flags = flags;
12791b34bc4aSbs135383 	rq.ioc_level = level;
12801b34bc4aSbs135383 
12811b34bc4aSbs135383 	return (smb_ctx_ioctl(ctx, SMBIOC_FINDVC, &rq));
12821b34bc4aSbs135383 }
12834bff34e3Sthurlow 
12844bff34e3Sthurlow /*
12854bff34e3Sthurlow  * adds a GSSAPI wrapper
12864bff34e3Sthurlow  */
12874bff34e3Sthurlow char *
12884bff34e3Sthurlow smb_ctx_tkt2gtok(uchar_t *tkt, ulong_t tktlen,
12894bff34e3Sthurlow     uchar_t **gtokp, ulong_t *gtoklenp)
12904bff34e3Sthurlow {
12914bff34e3Sthurlow 	ulong_t		bloblen = tktlen;
12924bff34e3Sthurlow 	ulong_t		len;
12934bff34e3Sthurlow 	uchar_t		krbapreq[2] = "\x01\x00"; /* see RFC 1964 */
12944bff34e3Sthurlow 	char 		*failure;
12954bff34e3Sthurlow 	uchar_t 	*blob = NULL;		/* result */
12964bff34e3Sthurlow 	uchar_t 	*b;
12974bff34e3Sthurlow 
12984bff34e3Sthurlow 	bloblen += sizeof (krbapreq);
12994bff34e3Sthurlow 	bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen;
13004bff34e3Sthurlow 	len = bloblen;
13014bff34e3Sthurlow 	bloblen = ASNDerCalcTokenLength(bloblen, bloblen);
13024bff34e3Sthurlow 	failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok malloc");
13034bff34e3Sthurlow 	if (!(blob = malloc(bloblen)))
13044bff34e3Sthurlow 		goto out;
13054bff34e3Sthurlow 	b = blob;
13064bff34e3Sthurlow 	b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len);
13074bff34e3Sthurlow 	b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5);
13084bff34e3Sthurlow 	memcpy(b, krbapreq, sizeof (krbapreq));
13094bff34e3Sthurlow 	b += sizeof (krbapreq);
13104bff34e3Sthurlow 	failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok insanity check");
13114bff34e3Sthurlow 	if (b + tktlen != blob + bloblen)
13124bff34e3Sthurlow 		goto out;
13134bff34e3Sthurlow 	memcpy(b, tkt, tktlen);
13144bff34e3Sthurlow 	*gtoklenp = bloblen;
13154bff34e3Sthurlow 	*gtokp = blob;
13164bff34e3Sthurlow 	failure = NULL;
13174bff34e3Sthurlow out:;
13184bff34e3Sthurlow 	if (blob && failure)
13194bff34e3Sthurlow 		free(blob);
13204bff34e3Sthurlow 	return (failure);
13214bff34e3Sthurlow }
13224bff34e3Sthurlow 
13234bff34e3Sthurlow 
13244bff34e3Sthurlow /*
13254bff34e3Sthurlow  * Initialization for Kerberos, pulled out of smb_ctx_principal2tkt.
13264bff34e3Sthurlow  * This just gets our cached credentials, if we have any.
13274bff34e3Sthurlow  * Based on the "klist" command.
13284bff34e3Sthurlow  */
13294bff34e3Sthurlow char *
13304bff34e3Sthurlow smb_ctx_krb5init(struct smb_ctx *ctx)
13314bff34e3Sthurlow {
13324bff34e3Sthurlow 	char *failure;
13334bff34e3Sthurlow 	krb5_error_code	kerr;
13344bff34e3Sthurlow 	krb5_context	kctx = NULL;
13354bff34e3Sthurlow 	krb5_ccache 	kcc = NULL;
13364bff34e3Sthurlow 	krb5_principal	kprin = NULL;
13374bff34e3Sthurlow 
13384bff34e3Sthurlow 	kerr = krb5_init_context(&kctx);
13394bff34e3Sthurlow 	if (kerr) {
13404bff34e3Sthurlow 		failure = "krb5_init_context";
13414bff34e3Sthurlow 		goto out;
13424bff34e3Sthurlow 	}
13434bff34e3Sthurlow 	ctx->ct_krb5ctx = kctx;
13444bff34e3Sthurlow 
13454bff34e3Sthurlow 	/* non-default would instead use krb5_cc_resolve */
13464bff34e3Sthurlow 	kerr = krb5_cc_default(kctx, &kcc);
13474bff34e3Sthurlow 	if (kerr) {
13484bff34e3Sthurlow 		failure = "krb5_cc_default";
13494bff34e3Sthurlow 		goto out;
13504bff34e3Sthurlow 	}
13514bff34e3Sthurlow 	ctx->ct_krb5cc = kcc;
13524bff34e3Sthurlow 
13534bff34e3Sthurlow 	/*
13544bff34e3Sthurlow 	 * Get the client principal (ticket),
13554bff34e3Sthurlow 	 * or find out if we don't have one.
13564bff34e3Sthurlow 	 */
13574bff34e3Sthurlow 	kerr = krb5_cc_get_principal(kctx, kcc, &kprin);
13584bff34e3Sthurlow 	if (kerr) {
13594bff34e3Sthurlow 		failure = "krb5_cc_get_principal";
13604bff34e3Sthurlow 		goto out;
13614bff34e3Sthurlow 	}
13624bff34e3Sthurlow 	ctx->ct_krb5cp = kprin;
13634bff34e3Sthurlow 
13644bff34e3Sthurlow 	if (smb_verbose) {
13654bff34e3Sthurlow 		fprintf(stderr, gettext("Ticket cache: %s:%s\n"),
13664bff34e3Sthurlow 		    krb5_cc_get_type(kctx, kcc),
13674bff34e3Sthurlow 		    krb5_cc_get_name(kctx, kcc));
13684bff34e3Sthurlow 	}
13694bff34e3Sthurlow 	failure = NULL;
13704bff34e3Sthurlow 
13714bff34e3Sthurlow out:
13724bff34e3Sthurlow 	return (failure);
13734bff34e3Sthurlow }
13744bff34e3Sthurlow 
13754bff34e3Sthurlow 
13764bff34e3Sthurlow /*
13774bff34e3Sthurlow  * See "Windows 2000 Kerberos Interoperability" paper by
13784bff34e3Sthurlow  * Christopher Nebergall.  RC4 HMAC is the W2K default but
13794bff34e3Sthurlow  * Samba support lagged (not due to Samba itself, but due to OS'
13804bff34e3Sthurlow  * Kerberos implementations.)
13814bff34e3Sthurlow  *
13824bff34e3Sthurlow  * Only session enc type should matter, not ticket enc type,
13834bff34e3Sthurlow  * per Sam Hartman on krbdev.
13844bff34e3Sthurlow  *
13854bff34e3Sthurlow  * Preauthentication failure topics in krb-protocol may help here...
13864bff34e3Sthurlow  * try "John Brezak" and/or "Clifford Neuman" too.
13874bff34e3Sthurlow  */
13884bff34e3Sthurlow static krb5_enctype kenctypes[] = {
13894bff34e3Sthurlow 	ENCTYPE_ARCFOUR_HMAC,	/* defined in Tiger krb5.h */
13904bff34e3Sthurlow 	ENCTYPE_DES_CBC_MD5,
13914bff34e3Sthurlow 	ENCTYPE_DES_CBC_CRC,
13924bff34e3Sthurlow 	ENCTYPE_NULL
13934bff34e3Sthurlow };
13944bff34e3Sthurlow 
13954bff34e3Sthurlow /*
13964bff34e3Sthurlow  * Obtain a kerberos ticket...
13974bff34e3Sthurlow  * (if TLD != "gov" then pray first)
13984bff34e3Sthurlow  */
13994bff34e3Sthurlow char *
14004bff34e3Sthurlow smb_ctx_principal2tkt(
14014bff34e3Sthurlow 	struct smb_ctx *ctx, char *prin,
14024bff34e3Sthurlow 	uchar_t **tktp, ulong_t *tktlenp)
14034bff34e3Sthurlow {
14044bff34e3Sthurlow 	char 		*failure;
14054bff34e3Sthurlow 	krb5_context	kctx = NULL;
14064bff34e3Sthurlow 	krb5_error_code	kerr;
14074bff34e3Sthurlow 	krb5_ccache	kcc = NULL;
14084bff34e3Sthurlow 	krb5_principal	kprin = NULL, cprn = NULL;
14094bff34e3Sthurlow 	krb5_creds	kcreds, *kcredsp = NULL;
14104bff34e3Sthurlow 	krb5_auth_context	kauth = NULL;
14114bff34e3Sthurlow 	krb5_data	kdata, kdata0;
14124bff34e3Sthurlow 	uchar_t 		*tkt;
14134bff34e3Sthurlow 
14144bff34e3Sthurlow 	memset((char *)&kcreds, 0, sizeof (kcreds));
14154bff34e3Sthurlow 	kdata0.length = 0;
14164bff34e3Sthurlow 
14174bff34e3Sthurlow 	/* These shoud have been done in smb_ctx_krb5init() */
14184bff34e3Sthurlow 	if (ctx->ct_krb5ctx == NULL ||
14194bff34e3Sthurlow 	    ctx->ct_krb5cc == NULL ||
14204bff34e3Sthurlow 	    ctx->ct_krb5cp == NULL) {
14214bff34e3Sthurlow 		failure = "smb_ctx_krb5init";
14224bff34e3Sthurlow 		goto out;
14234bff34e3Sthurlow 	}
14244bff34e3Sthurlow 	kctx = ctx->ct_krb5ctx;
14254bff34e3Sthurlow 	kcc  = ctx->ct_krb5cc;
14264bff34e3Sthurlow 	cprn = ctx->ct_krb5cp;
14274bff34e3Sthurlow 
14284bff34e3Sthurlow 	failure = "krb5_set_default_tgs_enctypes";
14294bff34e3Sthurlow 	if ((kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes)))
14304bff34e3Sthurlow 		goto out;
14314bff34e3Sthurlow 	/*
14324bff34e3Sthurlow 	 * The following is an unrolling of krb5_mk_req.  Something like:
14334bff34e3Sthurlow 	 * krb5_mk_req(kctx, &kauth, 0, service(prin), hostname(prin),
14344bff34e3Sthurlow 	 *		&kdata0, kcc, &kdata);)
14354bff34e3Sthurlow 	 * ...except we needed krb5_parse_name not krb5_sname_to_principal.
14364bff34e3Sthurlow 	 */
14374bff34e3Sthurlow 	failure = "krb5_parse_name";
14384bff34e3Sthurlow 	if ((kerr = krb5_parse_name(kctx, prin, &kprin)))
14394bff34e3Sthurlow 		goto out;
14404bff34e3Sthurlow 	failure = "krb5_copy_principal(server)";
14414bff34e3Sthurlow 	if ((kerr = krb5_copy_principal(kctx, kprin, &kcreds.server)))
14424bff34e3Sthurlow 		goto out;
14434bff34e3Sthurlow 	failure = "krb5_copy_principal(client)";
14444bff34e3Sthurlow 	if ((kerr = krb5_copy_principal(kctx, cprn, &kcreds.client)))
14454bff34e3Sthurlow 		goto out;
14464bff34e3Sthurlow 	failure = "krb5_get_credentials";
14474bff34e3Sthurlow 	if ((kerr = krb5_get_credentials(kctx, 0, kcc, &kcreds, &kcredsp)))
14484bff34e3Sthurlow 		goto out;
14494bff34e3Sthurlow 	failure = "krb5_mk_req_extended";
14504bff34e3Sthurlow 	if ((kerr = krb5_mk_req_extended(kctx, &kauth, 0, &kdata0, kcredsp,
14514bff34e3Sthurlow 	    &kdata)))
14524bff34e3Sthurlow 		goto out;
14534bff34e3Sthurlow 	failure = "malloc";
14544bff34e3Sthurlow 	if (!(tkt = malloc(kdata.length))) {
14554bff34e3Sthurlow 		krb5_free_data_contents(kctx, &kdata);
14564bff34e3Sthurlow 		goto out;
14574bff34e3Sthurlow 	}
14584bff34e3Sthurlow 	*tktlenp = kdata.length;
14594bff34e3Sthurlow 	memcpy(tkt, kdata.data, kdata.length);
14604bff34e3Sthurlow 	krb5_free_data_contents(kctx, &kdata);
14614bff34e3Sthurlow 	*tktp = tkt;
14624bff34e3Sthurlow 	failure = NULL;
14634bff34e3Sthurlow out:;
14644bff34e3Sthurlow 	if (kerr) {
14654bff34e3Sthurlow 		if (!failure)
14664bff34e3Sthurlow 			failure = "smb_ctx_principal2tkt";
14674bff34e3Sthurlow 		/*
14684bff34e3Sthurlow 		 * Avoid logging the typical "No credentials cache found"
14694bff34e3Sthurlow 		 */
14704bff34e3Sthurlow 		if (kerr != KRB5_FCC_NOFILE ||
14714bff34e3Sthurlow 		    strcmp(failure, "krb5_cc_get_principal"))
14724bff34e3Sthurlow 			com_err(__progname, kerr, failure);
14734bff34e3Sthurlow 	}
14744bff34e3Sthurlow 	if (kauth)
14754bff34e3Sthurlow 		krb5_auth_con_free(kctx, kauth);
14764bff34e3Sthurlow 	if (kcredsp)
14774bff34e3Sthurlow 		krb5_free_creds(kctx, kcredsp);
14784bff34e3Sthurlow 	if (kcreds.server || kcreds.client)
14794bff34e3Sthurlow 		krb5_free_cred_contents(kctx, &kcreds);
14804bff34e3Sthurlow 	if (kprin)
14814bff34e3Sthurlow 		krb5_free_principal(kctx, kprin);
14824bff34e3Sthurlow 
14834bff34e3Sthurlow 	/* Free kctx in smb_ctx_done */
14844bff34e3Sthurlow 
14854bff34e3Sthurlow 	return (failure);
14864bff34e3Sthurlow }
14874bff34e3Sthurlow 
14884bff34e3Sthurlow char *
14894bff34e3Sthurlow smb_ctx_principal2blob(
14904bff34e3Sthurlow 	struct smb_ctx *ctx,
14914bff34e3Sthurlow 	smbioc_ossn_t *ssn,
14924bff34e3Sthurlow 	char *prin)
14934bff34e3Sthurlow {
14944bff34e3Sthurlow 	int		rc = 0;
14954bff34e3Sthurlow 	char 		*failure;
14964bff34e3Sthurlow 	uchar_t 	*tkt = NULL;
14974bff34e3Sthurlow 	ulong_t		tktlen;
14984bff34e3Sthurlow 	uchar_t 	*gtok = NULL;		/* gssapi token */
14994bff34e3Sthurlow 	ulong_t		gtoklen;		/* gssapi token length */
15004bff34e3Sthurlow 	SPNEGO_TOKEN_HANDLE  stok = NULL;	/* spnego token */
15014bff34e3Sthurlow 	void 	*blob = NULL;		/* result */
15024bff34e3Sthurlow 	ulong_t		bloblen;		/* result length */
15034bff34e3Sthurlow 
15044bff34e3Sthurlow 	if ((failure = smb_ctx_principal2tkt(ctx, prin, &tkt, &tktlen)))
15054bff34e3Sthurlow 		goto out;
15064bff34e3Sthurlow 	if ((failure = smb_ctx_tkt2gtok(tkt, tktlen, &gtok, &gtoklen)))
15074bff34e3Sthurlow 		goto out;
15084bff34e3Sthurlow 	/*
15094bff34e3Sthurlow 	 * RFC says to send NegTokenTarg now.  So does MS docs.  But
15104bff34e3Sthurlow 	 * win2k gives ERRbaduid if we do...  we must send
15114bff34e3Sthurlow 	 * another NegTokenInit now!
15124bff34e3Sthurlow 	 */
15134bff34e3Sthurlow 	failure = "spnegoCreateNegTokenInit";
15144bff34e3Sthurlow 	if ((rc = spnegoCreateNegTokenInit(spnego_mech_oid_Kerberos_V5_Legacy,
15154bff34e3Sthurlow 	    0, gtok, gtoklen, NULL, 0, &stok)))
15164bff34e3Sthurlow 		goto out;
15174bff34e3Sthurlow 	failure = "spnegoTokenGetBinary(NULL)";
15184bff34e3Sthurlow 	rc = spnegoTokenGetBinary(stok, NULL, &bloblen);
15194bff34e3Sthurlow 	if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
15204bff34e3Sthurlow 		goto out;
15214bff34e3Sthurlow 	failure = "malloc";
15224bff34e3Sthurlow 	if (!(blob = malloc((size_t)bloblen)))
15234bff34e3Sthurlow 		goto out;
15244bff34e3Sthurlow 	/* No longer store length at start of blob. */
15254bff34e3Sthurlow 	/* *blob = bloblen; */
15264bff34e3Sthurlow 	failure = "spnegoTokenGetBinary";
15274bff34e3Sthurlow 	if ((rc = spnegoTokenGetBinary(stok, blob, &bloblen)))
15284bff34e3Sthurlow 		goto out;
15294bff34e3Sthurlow 	ssn->ioc_intoklen = bloblen;
15304bff34e3Sthurlow 	ssn->ioc_intok = blob;
15314bff34e3Sthurlow 	failure = NULL;
15324bff34e3Sthurlow out:;
15334bff34e3Sthurlow 	if (rc) {
15344bff34e3Sthurlow 		/* XXX better is to embed rc in failure */
15354bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
15364bff34e3Sthurlow 		    "spnego principal2blob error %d"), 0, -rc);
15374bff34e3Sthurlow 		if (!failure)
15384bff34e3Sthurlow 			failure = "spnego";
15394bff34e3Sthurlow 	}
15404bff34e3Sthurlow 	if (blob && failure)
15414bff34e3Sthurlow 		free(blob);
15424bff34e3Sthurlow 	if (stok)
15434bff34e3Sthurlow 		spnegoFreeData(stok);
15444bff34e3Sthurlow 	if (gtok)
15454bff34e3Sthurlow 		free(gtok);
15464bff34e3Sthurlow 	if (tkt)
15474bff34e3Sthurlow 		free(tkt);
15484bff34e3Sthurlow 	return (failure);
15494bff34e3Sthurlow }
15504bff34e3Sthurlow 
15514bff34e3Sthurlow 
15524bff34e3Sthurlow #if 0
15534bff34e3Sthurlow void
15544bff34e3Sthurlow prblob(uchar_t *b, size_t len)
15554bff34e3Sthurlow {
15564bff34e3Sthurlow 	while (len--)
15574bff34e3Sthurlow 		fprintf(stderr, "%02x", *b++);
15584bff34e3Sthurlow 	fprintf(stderr, "\n");
15594bff34e3Sthurlow }
15604bff34e3Sthurlow #endif
15614bff34e3Sthurlow 
15624bff34e3Sthurlow 
15634bff34e3Sthurlow /*
15644bff34e3Sthurlow  * We navigate the SPNEGO & ASN1 encoding to find a kerberos principal
15654bff34e3Sthurlow  * Note: driver no longer puts length at start of blob.
15664bff34e3Sthurlow  */
15674bff34e3Sthurlow char *
15684bff34e3Sthurlow smb_ctx_blob2principal(
15694bff34e3Sthurlow 	struct smb_ctx *ctx,
15704bff34e3Sthurlow 	smbioc_ossn_t *ssn,
15714bff34e3Sthurlow 	char **prinp)
15724bff34e3Sthurlow {
15734bff34e3Sthurlow 	uchar_t		*blob = ssn->ioc_outtok;
15744bff34e3Sthurlow 	size_t		len = ssn->ioc_outtoklen;
15754bff34e3Sthurlow 	int		rc = 0;
15764bff34e3Sthurlow 	SPNEGO_TOKEN_HANDLE	stok = NULL;
15774bff34e3Sthurlow 	int		indx = 0;
15784bff34e3Sthurlow 	char 		*failure;
15794bff34e3Sthurlow 	uchar_t		flags = 0;
15804bff34e3Sthurlow 	unsigned long	plen = 0;
15814bff34e3Sthurlow 	uchar_t 	*prin;
15824bff34e3Sthurlow 
15834bff34e3Sthurlow #if 0
15844bff34e3Sthurlow 	fprintf(stderr, "blob from negotiate:\n");
15854bff34e3Sthurlow 	prblob(blob, len);
15864bff34e3Sthurlow #endif
15874bff34e3Sthurlow 
15884bff34e3Sthurlow 	/* Skip the GUID */
15894bff34e3Sthurlow 	assert(len >= SMB_GUIDLEN);
15904bff34e3Sthurlow 	blob += SMB_GUIDLEN;
15914bff34e3Sthurlow 	len  -= SMB_GUIDLEN;
15924bff34e3Sthurlow 
15934bff34e3Sthurlow 	failure = "spnegoInitFromBinary";
15944bff34e3Sthurlow 	if ((rc = spnegoInitFromBinary(blob, len, &stok)))
15954bff34e3Sthurlow 		goto out;
15964bff34e3Sthurlow 	/*
15974bff34e3Sthurlow 	 * Needn't use new Kerberos OID - the Legacy one is fine.
15984bff34e3Sthurlow 	 */
15994bff34e3Sthurlow 	failure = "spnegoIsMechTypeAvailable";
16004bff34e3Sthurlow 	if (spnegoIsMechTypeAvailable(stok, spnego_mech_oid_Kerberos_V5_Legacy,
16014bff34e3Sthurlow 	    &indx))
16024bff34e3Sthurlow 		goto out;
16034bff34e3Sthurlow 	/*
16044bff34e3Sthurlow 	 * Ignoring optional context flags for now.  May want to pass
16054bff34e3Sthurlow 	 * them to krb5 layer.  XXX
16064bff34e3Sthurlow 	 */
16074bff34e3Sthurlow 	if (!spnegoGetContextFlags(stok, &flags))
16084bff34e3Sthurlow 		fprintf(stderr, dgettext(TEXT_DOMAIN,
16094bff34e3Sthurlow 		    "spnego context flags 0x%x\n"), flags);
16104bff34e3Sthurlow 	failure = "spnegoGetMechListMIC(NULL)";
16114bff34e3Sthurlow 	rc = spnegoGetMechListMIC(stok, NULL, &plen);
16124bff34e3Sthurlow 	if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
16134bff34e3Sthurlow 		goto out;
16144bff34e3Sthurlow 	failure = "malloc";
16154bff34e3Sthurlow 	if (!(prin = malloc(plen + 1)))
16164bff34e3Sthurlow 		goto out;
16174bff34e3Sthurlow 	failure = "spnegoGetMechListMIC";
16184bff34e3Sthurlow 	if ((rc = spnegoGetMechListMIC(stok, prin, &plen))) {
16194bff34e3Sthurlow 		free(prin);
16204bff34e3Sthurlow 		goto out;
16214bff34e3Sthurlow 	}
16224bff34e3Sthurlow 	prin[plen] = '\0';
16234bff34e3Sthurlow 	*prinp = (char *)prin;
16244bff34e3Sthurlow 	failure = NULL;
16254bff34e3Sthurlow out:;
16264bff34e3Sthurlow 	if (stok)
16274bff34e3Sthurlow 		spnegoFreeData(stok);
16284bff34e3Sthurlow 	if (rc) {
16294bff34e3Sthurlow 		/* XXX better is to embed rc in failure */
16304bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
16314bff34e3Sthurlow 		    "spnego blob2principal error %d"), 0, -rc);
16324bff34e3Sthurlow 		if (!failure)
16334bff34e3Sthurlow 			failure = "spnego";
16344bff34e3Sthurlow 	}
16354bff34e3Sthurlow 	return (failure);
16364bff34e3Sthurlow }
16374bff34e3Sthurlow 
16384bff34e3Sthurlow 
16394bff34e3Sthurlow int
16404bff34e3Sthurlow smb_ctx_negotiate(struct smb_ctx *ctx, int level, int flags, char *workgroup)
16414bff34e3Sthurlow {
16424bff34e3Sthurlow 	struct smbioc_lookup	rq;
16434bff34e3Sthurlow 	int	error = 0;
16444bff34e3Sthurlow 	char 	*failure = NULL;
16454bff34e3Sthurlow 	char	*principal = NULL;
16464bff34e3Sthurlow 	char c;
16474bff34e3Sthurlow 	int i;
16484bff34e3Sthurlow 	ssize_t *outtoklen;
16494bff34e3Sthurlow 	uchar_t *blob;
16504bff34e3Sthurlow 
16514bff34e3Sthurlow 	/*
16524bff34e3Sthurlow 	 * We leave ct_secblob set iff extended security
16534bff34e3Sthurlow 	 * negotiation succeeds.
16544bff34e3Sthurlow 	 */
16554bff34e3Sthurlow 	if (ctx->ct_secblob) {
16564bff34e3Sthurlow 		free(ctx->ct_secblob);
16574bff34e3Sthurlow 		ctx->ct_secblob = NULL;
16584bff34e3Sthurlow 	}
16594bff34e3Sthurlow #ifdef XXX
16604bff34e3Sthurlow 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
16614bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
16624bff34e3Sthurlow 		    "smb_ctx_lookup() data is not resolved"), 0);
16634bff34e3Sthurlow 		return (EINVAL);
16644bff34e3Sthurlow 	}
16654bff34e3Sthurlow #endif
16664bff34e3Sthurlow 	if ((error = smb_ctx_gethandle(ctx)))
16674bff34e3Sthurlow 		return (error);
16684bff34e3Sthurlow 
16694bff34e3Sthurlow 	bzero(&rq, sizeof (rq));
16704bff34e3Sthurlow 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
16714bff34e3Sthurlow 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
16724bff34e3Sthurlow 
16734bff34e3Sthurlow 	/*
16744bff34e3Sthurlow 	 * Find out if we have a Kerberos ticket,
16754bff34e3Sthurlow 	 * and only offer SPNEGO if we have one.
16764bff34e3Sthurlow 	 */
16774bff34e3Sthurlow 	failure = smb_ctx_krb5init(ctx);
16784bff34e3Sthurlow 	if (failure) {
16794bff34e3Sthurlow 		if (smb_verbose)
16804bff34e3Sthurlow 			smb_error(failure, 0);
16814bff34e3Sthurlow 		goto out;
16824bff34e3Sthurlow 	}
16834bff34e3Sthurlow 
16844bff34e3Sthurlow 	rq.ioc_flags = flags;
16854bff34e3Sthurlow 	rq.ioc_level = level;
16864bff34e3Sthurlow 	rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
16874bff34e3Sthurlow 	error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
16884bff34e3Sthurlow 	if (error) {
16894bff34e3Sthurlow 		failure = dgettext(TEXT_DOMAIN, "negotiate failed");
16904bff34e3Sthurlow 		smb_error(failure, error);
16914bff34e3Sthurlow 		if (error == ETIMEDOUT)
16924bff34e3Sthurlow 			return (error);
16934bff34e3Sthurlow 		goto out;
16944bff34e3Sthurlow 	}
16954bff34e3Sthurlow 	/*
16964bff34e3Sthurlow 	 * If the server capabilities did not include
16974bff34e3Sthurlow 	 * SMB_CAP_EXT_SECURITY then the driver clears
16984bff34e3Sthurlow 	 * the flag SMBVOPT_EXT_SEC for us.
16994bff34e3Sthurlow 	 * XXX: should add the capabilities to ioc_ssn
17004bff34e3Sthurlow 	 * XXX: see comment in driver - smb_usr.c
17014bff34e3Sthurlow 	 */
17024bff34e3Sthurlow 	failure = dgettext(TEXT_DOMAIN, "SPNEGO unsupported");
17034bff34e3Sthurlow 	if ((rq.ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC) == 0) {
17044bff34e3Sthurlow 		if (smb_verbose)
17054bff34e3Sthurlow 			smb_error(failure, 0);
17064bff34e3Sthurlow 		/*
17074bff34e3Sthurlow 		 * Do regular (old style) NTLM or NTLMv2
17084bff34e3Sthurlow 		 * Nothing more to do here in negotiate.
17094bff34e3Sthurlow 		 */
17104bff34e3Sthurlow 		return (0);
17114bff34e3Sthurlow 	}
17124bff34e3Sthurlow 
17134bff34e3Sthurlow 	/*
17144bff34e3Sthurlow 	 * Capabilities DO include SMB_CAP_EXT_SECURITY,
17154bff34e3Sthurlow 	 * so this should be an SPNEGO security blob.
17164bff34e3Sthurlow 	 * Parse the ASN.1/DER, prepare response(s).
17174bff34e3Sthurlow 	 * XXX: Handle STATUS_MORE_PROCESSING_REQUIRED?
17184bff34e3Sthurlow 	 * XXX: Requires additional session setup calls.
17194bff34e3Sthurlow 	 */
17204bff34e3Sthurlow 	if (rq.ioc_ssn.ioc_outtoklen <= SMB_GUIDLEN)
17214bff34e3Sthurlow 		goto out;
17224bff34e3Sthurlow 	/* some servers send padding junk */
17234bff34e3Sthurlow 	blob = rq.ioc_ssn.ioc_outtok;
17244bff34e3Sthurlow 	if (blob[0] == 0)
17254bff34e3Sthurlow 		goto out;
17264bff34e3Sthurlow 
17274bff34e3Sthurlow 	failure = smb_ctx_blob2principal(
17284bff34e3Sthurlow 	    ctx, &rq.ioc_ssn, &principal);
17294bff34e3Sthurlow 	if (failure)
17304bff34e3Sthurlow 		goto out;
17314bff34e3Sthurlow 	failure = smb_ctx_principal2blob(
17324bff34e3Sthurlow 	    ctx, &rq.ioc_ssn, principal);
17334bff34e3Sthurlow 	if (failure)
17344bff34e3Sthurlow 		goto out;
17354bff34e3Sthurlow 
17364bff34e3Sthurlow 	/* Success! Save the blob to send next. */
17374bff34e3Sthurlow 	ctx->ct_secblob = rq.ioc_ssn.ioc_intok;
17384bff34e3Sthurlow 	ctx->ct_secbloblen = rq.ioc_ssn.ioc_intoklen;
17394bff34e3Sthurlow 	rq.ioc_ssn.ioc_intok = NULL;
17404bff34e3Sthurlow 
17414bff34e3Sthurlow out:
17424bff34e3Sthurlow 	if (principal)
17434bff34e3Sthurlow 		free(principal);
17444bff34e3Sthurlow 	if (rq.ioc_ssn.ioc_intok)
17454bff34e3Sthurlow 		free(rq.ioc_ssn.ioc_intok);
17464bff34e3Sthurlow 	if (rq.ioc_ssn.ioc_outtok)
17474bff34e3Sthurlow 		free(rq.ioc_ssn.ioc_outtok);
17484bff34e3Sthurlow 	if (!failure)
17494bff34e3Sthurlow 		return (0);		/* Success! */
17504bff34e3Sthurlow 
17514bff34e3Sthurlow 	/*
17524bff34e3Sthurlow 	 * Negotiate failed with "extended security".
17534bff34e3Sthurlow 	 *
17544bff34e3Sthurlow 	 * XXX: If we are doing SPNEGO correctly,
17554bff34e3Sthurlow 	 * we should never get here unless the user
17564bff34e3Sthurlow 	 * supplied invalid authentication data,
17574bff34e3Sthurlow 	 * or we saw some kind of protocol error.
17584bff34e3Sthurlow 	 *
17594bff34e3Sthurlow 	 * XXX: The error message below should be
17604bff34e3Sthurlow 	 * XXX: unconditional (remove "if verbose")
17614bff34e3Sthurlow 	 * XXX: but not until we have "NTLMSSP"
17624bff34e3Sthurlow 	 * Avoid spew for anticipated failure modes
17634bff34e3Sthurlow 	 * but enable this with the verbose flag
17644bff34e3Sthurlow 	 */
17654bff34e3Sthurlow 	if (smb_verbose) {
17664bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
17674bff34e3Sthurlow 		    "%s (extended security negotiate)"), error, failure);
17684bff34e3Sthurlow 	}
17694bff34e3Sthurlow 
17704bff34e3Sthurlow 	/*
17714bff34e3Sthurlow 	 * XXX: Try again using NTLM (or NTLMv2)
17724bff34e3Sthurlow 	 * XXX: Normal clients don't do this.
17734bff34e3Sthurlow 	 * XXX: Should just return an error, but
17744bff34e3Sthurlow 	 * keep the fall-back to NTLM for now.
17754bff34e3Sthurlow 	 *
17764bff34e3Sthurlow 	 * Start over with a new connection.
17774bff34e3Sthurlow 	 */
17784bff34e3Sthurlow 	if ((error = smb_ctx_gethandle(ctx)))
17794bff34e3Sthurlow 		return (error);
17804bff34e3Sthurlow 	bzero(&rq, sizeof (rq));
17814bff34e3Sthurlow 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
17824bff34e3Sthurlow 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
17834bff34e3Sthurlow 	rq.ioc_flags = flags;
17844bff34e3Sthurlow 	rq.ioc_level = level;
17854bff34e3Sthurlow 	/* Note: NO SMBVOPT_EXT_SEC */
17864bff34e3Sthurlow 	error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
17874bff34e3Sthurlow 	if (error) {
17884bff34e3Sthurlow 		failure = dgettext(TEXT_DOMAIN, "negotiate failed");
17894bff34e3Sthurlow 		smb_error(failure, error);
17904bff34e3Sthurlow 		rpc_cleanup_smbctx(ctx);
17914bff34e3Sthurlow 		close(ctx->ct_fd);
17924bff34e3Sthurlow 		ctx->ct_fd = -1;
17934bff34e3Sthurlow 		return (error);
17944bff34e3Sthurlow 	}
17954bff34e3Sthurlow 
17964bff34e3Sthurlow 	/*
17974bff34e3Sthurlow 	 * Used to copy the workgroup out of the SMB_NEGOTIATE response
17984bff34e3Sthurlow 	 * here, to default our domain name to be the same as the server.
17994bff34e3Sthurlow 	 * Not a good idea: Unnecessary at best, and sometimes wrong, i.e.
18004bff34e3Sthurlow 	 * when our account is in a trusted domain.
18014bff34e3Sthurlow 	 */
18024bff34e3Sthurlow 
18034bff34e3Sthurlow 	return (error);
18044bff34e3Sthurlow }
18054bff34e3Sthurlow 
18064bff34e3Sthurlow 
18074bff34e3Sthurlow int
18084bff34e3Sthurlow smb_ctx_tdis(struct smb_ctx *ctx)
18094bff34e3Sthurlow {
18104bff34e3Sthurlow 	struct smbioc_lookup rq; /* XXX may be used, someday */
18114bff34e3Sthurlow 	int error = 0;
18124bff34e3Sthurlow 
18134bff34e3Sthurlow 	if (ctx->ct_fd < 0) {
18144bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
18154bff34e3Sthurlow 		    "tree disconnect without handle?!"), 0);
18164bff34e3Sthurlow 		return (EINVAL);
18174bff34e3Sthurlow 	}
18184bff34e3Sthurlow 	if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
18194bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
18204bff34e3Sthurlow 		    "tree disconnect without session?!"), 0);
18214bff34e3Sthurlow 		return (EINVAL);
18224bff34e3Sthurlow 	}
18234bff34e3Sthurlow 	bzero(&rq, sizeof (rq));
18244bff34e3Sthurlow 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
18254bff34e3Sthurlow 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
18264bff34e3Sthurlow 	if (ioctl(ctx->ct_fd, SMBIOC_TDIS, &rq) == -1) {
18274bff34e3Sthurlow 		error = errno;
18284bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
18294bff34e3Sthurlow 		    "tree disconnect failed"), error);
18304bff34e3Sthurlow 	}
18314bff34e3Sthurlow 	return (error);
18324bff34e3Sthurlow }
18334bff34e3Sthurlow 
18344bff34e3Sthurlow 
18354bff34e3Sthurlow int
18364bff34e3Sthurlow smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
18374bff34e3Sthurlow {
18384bff34e3Sthurlow 	struct smbioc_lookup rq;
18394bff34e3Sthurlow 	int error = 0;
18404bff34e3Sthurlow 	char 	*failure = NULL;
18414bff34e3Sthurlow 
18424bff34e3Sthurlow 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
18434bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
18444bff34e3Sthurlow 		    "smb_ctx_lookup() data is not resolved"), 0);
18454bff34e3Sthurlow 		return (EINVAL);
18464bff34e3Sthurlow 	}
18474bff34e3Sthurlow 	if (ctx->ct_fd < 0) {
18484bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
18494bff34e3Sthurlow 		    "handle from smb_ctx_nego() gone?!"), 0);
18504bff34e3Sthurlow 		return (EINVAL);
18514bff34e3Sthurlow 	}
18524bff34e3Sthurlow 	if (!(flags & SMBLK_CREATE))
18534bff34e3Sthurlow 		return (0);
18544bff34e3Sthurlow 	bzero(&rq, sizeof (rq));
18554bff34e3Sthurlow 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
18564bff34e3Sthurlow 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
18574bff34e3Sthurlow 	rq.ioc_flags = flags;
18584bff34e3Sthurlow 	rq.ioc_level = level;
18594bff34e3Sthurlow 
18604bff34e3Sthurlow 	/*
18614bff34e3Sthurlow 	 * Iff we have a security blob, we're using
18624bff34e3Sthurlow 	 * extended security...
18634bff34e3Sthurlow 	 */
18644bff34e3Sthurlow 	if (ctx->ct_secblob) {
18654bff34e3Sthurlow 		rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
18664bff34e3Sthurlow 		if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
18674bff34e3Sthurlow 			rq.ioc_ssn.ioc_intok = ctx->ct_secblob;
18684bff34e3Sthurlow 			rq.ioc_ssn.ioc_intoklen = ctx->ct_secbloblen;
18694bff34e3Sthurlow 			error = smb_ctx_ioctl(ctx, SMBIOC_SSNSETUP, &rq);
18704bff34e3Sthurlow 		}
18714bff34e3Sthurlow 		rq.ioc_ssn.ioc_intok = NULL;
18724bff34e3Sthurlow 		if (error) {
18734bff34e3Sthurlow 			failure = dgettext(TEXT_DOMAIN,
18744bff34e3Sthurlow 			    "session setup failed");
18754bff34e3Sthurlow 		} else {
18764bff34e3Sthurlow 			ctx->ct_flags |= SMBCF_SSNACTIVE;
18774bff34e3Sthurlow 			if ((error = smb_ctx_ioctl(ctx, SMBIOC_TCON, &rq)))
18784bff34e3Sthurlow 				failure = dgettext(TEXT_DOMAIN,
18794bff34e3Sthurlow 				    "tree connect failed");
18804bff34e3Sthurlow 		}
18814bff34e3Sthurlow 		if (rq.ioc_ssn.ioc_intok)
18824bff34e3Sthurlow 			free(rq.ioc_ssn.ioc_intok);
18834bff34e3Sthurlow 		if (rq.ioc_ssn.ioc_outtok)
18844bff34e3Sthurlow 			free(rq.ioc_ssn.ioc_outtok);
18854bff34e3Sthurlow 		if (!failure)
18864bff34e3Sthurlow 			return (0);
18874bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
18884bff34e3Sthurlow 		    "%s (extended security lookup2)"), error, failure);
18894bff34e3Sthurlow 		/* unwise to failback to NTLM now */
18904bff34e3Sthurlow 		return (error);
18914bff34e3Sthurlow 	}
18924bff34e3Sthurlow 
18934bff34e3Sthurlow 	/*
18944bff34e3Sthurlow 	 * Otherwise we're doing plain old NTLM
18954bff34e3Sthurlow 	 */
18964bff34e3Sthurlow 	if ((ctx->ct_flags & SMBCF_SSNACTIVE) == 0) {
18974bff34e3Sthurlow 		/*
18984bff34e3Sthurlow 		 * This is the magic that tells the driver to
18994bff34e3Sthurlow 		 * copy the password from the keychain, and
19004bff34e3Sthurlow 		 * whether to use the system name or the
19014bff34e3Sthurlow 		 * account domain to lookup the keychain.
19024bff34e3Sthurlow 		 */
19034bff34e3Sthurlow 		if (ctx->ct_flags & SMBCF_KCFOUND)
19044bff34e3Sthurlow 			rq.ioc_ssn.ioc_opt |= SMBVOPT_USE_KEYCHAIN;
19054bff34e3Sthurlow 		if (ctx->ct_flags & SMBCF_KCDOMAIN)
19064bff34e3Sthurlow 			rq.ioc_ssn.ioc_opt |= SMBVOPT_KC_DOMAIN;
19074bff34e3Sthurlow 		if (ioctl(ctx->ct_fd, SMBIOC_SSNSETUP, &rq) < 0) {
19084bff34e3Sthurlow 			error = errno;
19094bff34e3Sthurlow 			failure = dgettext(TEXT_DOMAIN, "session setup");
19104bff34e3Sthurlow 			goto out;
19114bff34e3Sthurlow 		}
19124bff34e3Sthurlow 		ctx->ct_flags |= SMBCF_SSNACTIVE;
19134bff34e3Sthurlow 	}
19144bff34e3Sthurlow 	if (ioctl(ctx->ct_fd, SMBIOC_TCON, &rq) == -1) {
19154bff34e3Sthurlow 		error = errno;
19164bff34e3Sthurlow 		failure = dgettext(TEXT_DOMAIN, "tree connect");
19174bff34e3Sthurlow 	}
19184bff34e3Sthurlow 
19194bff34e3Sthurlow out:
19204bff34e3Sthurlow 	if (failure) {
19214bff34e3Sthurlow 		error = errno;
19224bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
19234bff34e3Sthurlow 		    "%s phase failed"), error, failure);
19244bff34e3Sthurlow 	}
19254bff34e3Sthurlow 	return (error);
19264bff34e3Sthurlow }
19274bff34e3Sthurlow 
19284bff34e3Sthurlow /*
19294bff34e3Sthurlow  * Return the hflags2 word for an smb_ctx.
19304bff34e3Sthurlow  */
19314bff34e3Sthurlow int
19324bff34e3Sthurlow smb_ctx_flags2(struct smb_ctx *ctx)
19334bff34e3Sthurlow {
19344bff34e3Sthurlow 	uint16_t flags2;
19354bff34e3Sthurlow 
19364bff34e3Sthurlow 	if (ioctl(ctx->ct_fd, SMBIOC_FLAGS2, &flags2) == -1) {
19374bff34e3Sthurlow 		smb_error(dgettext(TEXT_DOMAIN,
19384bff34e3Sthurlow 		    "can't get flags2 for a session"), errno);
19394bff34e3Sthurlow 		return (-1);
19404bff34e3Sthurlow 	}
19414bff34e3Sthurlow 	return (flags2);
19424bff34e3Sthurlow }
19434bff34e3Sthurlow 
19444bff34e3Sthurlow /*
19454bff34e3Sthurlow  * level values:
19464bff34e3Sthurlow  * 0 - default
19474bff34e3Sthurlow  * 1 - server
19484bff34e3Sthurlow  * 2 - server:user
19494bff34e3Sthurlow  * 3 - server:user:share
19504bff34e3Sthurlow  */
19514bff34e3Sthurlow static int
19524bff34e3Sthurlow smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
19534bff34e3Sthurlow {
19544bff34e3Sthurlow 	char *p;
19554bff34e3Sthurlow 	int error;
19564bff34e3Sthurlow 
19574bff34e3Sthurlow #ifdef NOT_DEFINED
19584bff34e3Sthurlow 	if (level > 0) {
19594bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "charsets", &p);
19604bff34e3Sthurlow 		if (p) {
19614bff34e3Sthurlow 			error = smb_ctx_setcharset(ctx, p);
19624bff34e3Sthurlow 			if (error)
19634bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
19644bff34e3Sthurlow 	"charset specification in the section '%s' ignored"),
19654bff34e3Sthurlow 				    error, sname);
19664bff34e3Sthurlow 		}
19674bff34e3Sthurlow 	}
19684bff34e3Sthurlow #endif
19694bff34e3Sthurlow 
19704bff34e3Sthurlow 	if (level <= 1) {
19714bff34e3Sthurlow 		/* Section is: [default] or [server] */
19724bff34e3Sthurlow 
19734bff34e3Sthurlow 		rc_getint(smb_rc, sname, "timeout",
19744bff34e3Sthurlow 		    &ctx->ct_ssn.ioc_timeout);
19754bff34e3Sthurlow 
19764bff34e3Sthurlow #ifdef NOT_DEFINED
19774bff34e3Sthurlow 		rc_getint(smb_rc, sname, "retry_count",
19784bff34e3Sthurlow 		    &ctx->ct_ssn.ioc_retrycount);
19794bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "use_negprot_domain", &p);
19804bff34e3Sthurlow 		if (p && strcmp(p, "NO") == 0)
19814bff34e3Sthurlow 			ctx->ct_flags |= SMBCF_NONEGDOM;
19824bff34e3Sthurlow #endif
19834bff34e3Sthurlow 
19844bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "minauth", &p);
19854bff34e3Sthurlow 		if (p) {
19864bff34e3Sthurlow 			/*
19874bff34e3Sthurlow 			 * "minauth" was set in this section; override
19884bff34e3Sthurlow 			 * the current minimum authentication setting.
19894bff34e3Sthurlow 			 */
19904bff34e3Sthurlow 			ctx->ct_ssn.ioc_opt &= ~SMBVOPT_MINAUTH;
19914bff34e3Sthurlow 			if (strcmp(p, "kerberos") == 0) {
19924bff34e3Sthurlow 				/*
19934bff34e3Sthurlow 				 * Don't fall back to NTLMv2, NTLMv1, or
19944bff34e3Sthurlow 				 * a clear text password.
19954bff34e3Sthurlow 				 */
19964bff34e3Sthurlow 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_KERBEROS;
19974bff34e3Sthurlow 			} else if (strcmp(p, "ntlmv2") == 0) {
19984bff34e3Sthurlow 				/*
19994bff34e3Sthurlow 				 * Don't fall back to NTLMv1 or a clear
20004bff34e3Sthurlow 				 * text password.
20014bff34e3Sthurlow 				 */
20024bff34e3Sthurlow 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLMV2;
20034bff34e3Sthurlow 			} else if (strcmp(p, "ntlm") == 0) {
20044bff34e3Sthurlow 				/*
20054bff34e3Sthurlow 				 * Don't send the LM response over the wire.
20064bff34e3Sthurlow 				 */
20074bff34e3Sthurlow 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLM;
20084bff34e3Sthurlow 			} else if (strcmp(p, "lm") == 0) {
20094bff34e3Sthurlow 				/*
20104bff34e3Sthurlow 				 * Fail if the server doesn't do encrypted
20114bff34e3Sthurlow 				 * passwords.
20124bff34e3Sthurlow 				 */
20134bff34e3Sthurlow 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_LM;
20144bff34e3Sthurlow 			} else if (strcmp(p, "none") == 0) {
20154bff34e3Sthurlow 				/*
20164bff34e3Sthurlow 				 * Anything goes.
20174bff34e3Sthurlow 				 * (The following statement should be
20184bff34e3Sthurlow 				 * optimized away.)
20194bff34e3Sthurlow 				 */
20204bff34e3Sthurlow 				/* LINTED */
20214bff34e3Sthurlow 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NONE;
20224bff34e3Sthurlow 			} else {
20234bff34e3Sthurlow 				/*
20244bff34e3Sthurlow 				 * Unknown minimum authentication level.
20254bff34e3Sthurlow 				 */
20264bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
20274bff34e3Sthurlow "invalid minimum authentication level \"%s\" specified in the section %s"),
20284bff34e3Sthurlow 				    0, p, sname);
20294bff34e3Sthurlow 				return (EINVAL);
20304bff34e3Sthurlow 			}
20314bff34e3Sthurlow 		}
20324bff34e3Sthurlow 
2033*9c9af259SGordon Ross 		rc_getstringptr(smb_rc, sname, "signing", &p);
2034*9c9af259SGordon Ross 		if (p) {
2035*9c9af259SGordon Ross 			/*
2036*9c9af259SGordon Ross 			 * "signing" was set in this section; override
2037*9c9af259SGordon Ross 			 * the current signing settings.
2038*9c9af259SGordon Ross 			 */
2039*9c9af259SGordon Ross 			ctx->ct_ssn.ioc_opt &= ~SMBVOPT_SIGNING_MASK;
2040*9c9af259SGordon Ross 			if (strcmp(p, "disabled") == 0) {
2041*9c9af259SGordon Ross 				/* leave flags zero (expr for lint) */
2042*9c9af259SGordon Ross 				(void) ctx->ct_ssn.ioc_opt;
2043*9c9af259SGordon Ross 			} else if (strcmp(p, "enabled") == 0) {
2044*9c9af259SGordon Ross 				ctx->ct_ssn.ioc_opt |=
2045*9c9af259SGordon Ross 				    SMBVOPT_SIGNING_ENABLED;
2046*9c9af259SGordon Ross 			} else if (strcmp(p, "required") == 0) {
2047*9c9af259SGordon Ross 				ctx->ct_ssn.ioc_opt |=
2048*9c9af259SGordon Ross 				    SMBVOPT_SIGNING_ENABLED |
2049*9c9af259SGordon Ross 				    SMBVOPT_SIGNING_REQUIRED;
2050*9c9af259SGordon Ross 			} else {
2051*9c9af259SGordon Ross 				/*
2052*9c9af259SGordon Ross 				 * Unknown "signing" value.
2053*9c9af259SGordon Ross 				 */
2054*9c9af259SGordon Ross 				smb_error(dgettext(TEXT_DOMAIN,
2055*9c9af259SGordon Ross "invalid signing policy \"%s\" specified in the section %s"),
2056*9c9af259SGordon Ross 				    0, p, sname);
2057*9c9af259SGordon Ross 				return (EINVAL);
2058*9c9af259SGordon Ross 			}
2059*9c9af259SGordon Ross 		}
2060*9c9af259SGordon Ross 
20614bff34e3Sthurlow 		/*
20624bff34e3Sthurlow 		 * Domain name.  Allow both keywords:
20634bff34e3Sthurlow 		 * "workgroup", "domain"
20644bff34e3Sthurlow 		 *
20654bff34e3Sthurlow 		 * Note: these are NOT marked "from CMD".
20664bff34e3Sthurlow 		 * See long comment at smb_ctx_init()
20674bff34e3Sthurlow 		 */
20684bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "workgroup", &p);
20694bff34e3Sthurlow 		if (p) {
20704bff34e3Sthurlow 			nls_str_upper(p, p);
20714bff34e3Sthurlow 			error = smb_ctx_setworkgroup(ctx, p, 0);
20724bff34e3Sthurlow 			if (error)
20734bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
20744bff34e3Sthurlow 				    "workgroup specification in the "
20754bff34e3Sthurlow 				    "section '%s' ignored"), error, sname);
20764bff34e3Sthurlow 		}
20774bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "domain", &p);
20784bff34e3Sthurlow 		if (p) {
20794bff34e3Sthurlow 			nls_str_upper(p, p);
20804bff34e3Sthurlow 			error = smb_ctx_setworkgroup(ctx, p, 0);
20814bff34e3Sthurlow 			if (error)
20824bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
20834bff34e3Sthurlow 				    "domain specification in the "
20844bff34e3Sthurlow 				    "section '%s' ignored"), error, sname);
20854bff34e3Sthurlow 		}
20864bff34e3Sthurlow 
20874bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "user", &p);
20884bff34e3Sthurlow 		if (p) {
20894bff34e3Sthurlow 			error = smb_ctx_setuser(ctx, p, 0);
20904bff34e3Sthurlow 			if (error)
20914bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
20924bff34e3Sthurlow 				    "user specification in the "
20934bff34e3Sthurlow 				    "section '%s' ignored"), error, sname);
20944bff34e3Sthurlow 		}
20954bff34e3Sthurlow 	}
20964bff34e3Sthurlow 
20974bff34e3Sthurlow 	if (level == 1) {
20984bff34e3Sthurlow 		/* Section is: [server] */
20994bff34e3Sthurlow 		rc_getstringptr(smb_rc, sname, "addr", &p);
21004bff34e3Sthurlow 		if (p) {
21014bff34e3Sthurlow 			error = smb_ctx_setsrvaddr(ctx, p);
21024bff34e3Sthurlow 			if (error) {
21034bff34e3Sthurlow 				smb_error(dgettext(TEXT_DOMAIN,
21044bff34e3Sthurlow 				    "invalid address specified in section %s"),
21054bff34e3Sthurlow 				    0, sname);
21064bff34e3Sthurlow 				return (error);
21074bff34e3Sthurlow 			}
21084bff34e3Sthurlow 		}
21094bff34e3Sthurlow 	}
21104bff34e3Sthurlow 
21114bff34e3Sthurlow 	rc_getstringptr(smb_rc, sname, "password", &p);
21124bff34e3Sthurlow 	if (p) {
21134bff34e3Sthurlow 		error = smb_ctx_setpassword(ctx, p, 0);
21144bff34e3Sthurlow 		if (error)
21154bff34e3Sthurlow 			smb_error(dgettext(TEXT_DOMAIN,
21164bff34e3Sthurlow 	    "password specification in the section '%s' ignored"),
21174bff34e3Sthurlow 			    error, sname);
21184bff34e3Sthurlow 	}
21194bff34e3Sthurlow 
21204bff34e3Sthurlow 	return (0);
21214bff34e3Sthurlow }
21224bff34e3Sthurlow 
21234bff34e3Sthurlow /*
21244bff34e3Sthurlow  * read rc file as follows:
21254bff34e3Sthurlow  * 0: read [default] section
21264bff34e3Sthurlow  * 1: override with [server] section
21274bff34e3Sthurlow  * 2: override with [server:user] section
21284bff34e3Sthurlow  * 3: override with [server:user:share] section
21294bff34e3Sthurlow  * Since absence of rcfile is not fatal, silently ignore this fact.
21304bff34e3Sthurlow  * smb_rc file should be closed by caller.
21314bff34e3Sthurlow  */
21324bff34e3Sthurlow int
21334bff34e3Sthurlow smb_ctx_readrc(struct smb_ctx *ctx)
21344bff34e3Sthurlow {
21354bff34e3Sthurlow 	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN +
21364bff34e3Sthurlow 	    SMB_MAXSHARENAMELEN + 4];
21374bff34e3Sthurlow 
21384bff34e3Sthurlow 	if (smb_open_rcfile(ctx) != 0)
21394bff34e3Sthurlow 		goto done;
21404bff34e3Sthurlow 
21414bff34e3Sthurlow 	/*
21424bff34e3Sthurlow 	 * default parameters (level=0)
21434bff34e3Sthurlow 	 */
21444bff34e3Sthurlow 	smb_ctx_readrcsection(ctx, "default", 0);
21454bff34e3Sthurlow 	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
21464bff34e3Sthurlow 
21474bff34e3Sthurlow 	/*
21484bff34e3Sthurlow 	 * If we don't have a server name, we can't read any of the
21494bff34e3Sthurlow 	 * [server...] sections.
21504bff34e3Sthurlow 	 */
21514bff34e3Sthurlow 	if (ctx->ct_ssn.ioc_srvname[0] == 0)
21524bff34e3Sthurlow 		goto done;
21534bff34e3Sthurlow 
21544bff34e3Sthurlow 	/*
21554bff34e3Sthurlow 	 * SERVER parameters.
21564bff34e3Sthurlow 	 */
21574bff34e3Sthurlow 	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
21584bff34e3Sthurlow 
21594bff34e3Sthurlow 	/*
21604bff34e3Sthurlow 	 * If we don't have a user name, we can't read any of the
21614bff34e3Sthurlow 	 * [server:user...] sections.
21624bff34e3Sthurlow 	 */
21634bff34e3Sthurlow 	if (ctx->ct_ssn.ioc_user[0] == 0)
21644bff34e3Sthurlow 		goto done;
21654bff34e3Sthurlow 
21664bff34e3Sthurlow 	/*
21674bff34e3Sthurlow 	 * SERVER:USER parameters
21684bff34e3Sthurlow 	 */
21694bff34e3Sthurlow 	snprintf(sname, sizeof (sname), "%s:%s",
21704bff34e3Sthurlow 	    ctx->ct_ssn.ioc_srvname,
21714bff34e3Sthurlow 	    ctx->ct_ssn.ioc_user);
21724bff34e3Sthurlow 	smb_ctx_readrcsection(ctx, sname, 2);
21734bff34e3Sthurlow 
21744bff34e3Sthurlow 	/*
21754bff34e3Sthurlow 	 * If we don't have a share name, we can't read any of the
21764bff34e3Sthurlow 	 * [server:user:share] sections.
21774bff34e3Sthurlow 	 */
21784bff34e3Sthurlow 	if (ctx->ct_sh.ioc_share[0] != 0) {
21794bff34e3Sthurlow 		/*
21804bff34e3Sthurlow 		 * SERVER:USER:SHARE parameters
21814bff34e3Sthurlow 		 */
21824bff34e3Sthurlow 		snprintf(sname, sizeof (sname), "%s:%s:%s",
21834bff34e3Sthurlow 		    ctx->ct_ssn.ioc_srvname,
21844bff34e3Sthurlow 		    ctx->ct_ssn.ioc_user,
21854bff34e3Sthurlow 		    ctx->ct_sh.ioc_share);
21864bff34e3Sthurlow 		smb_ctx_readrcsection(ctx, sname, 3);
21874bff34e3Sthurlow 	}
21884bff34e3Sthurlow 
21894bff34e3Sthurlow done:
21904bff34e3Sthurlow 	if (smb_debug)
21914bff34e3Sthurlow 		dump_ctx("after smb_ctx_readrc", ctx);
21924bff34e3Sthurlow 
21934bff34e3Sthurlow 	return (0);
21944bff34e3Sthurlow }
2195