187866Ssheldonh/*
295267Ssheldonh * Copyright (c) 2000-2002, Boris Popov
387866Ssheldonh * All rights reserved.
487866Ssheldonh *
587866Ssheldonh * Redistribution and use in source and binary forms, with or without
687866Ssheldonh * modification, are permitted provided that the following conditions
787866Ssheldonh * are met:
887866Ssheldonh * 1. Redistributions of source code must retain the above copyright
987866Ssheldonh *    notice, this list of conditions and the following disclaimer.
1087866Ssheldonh * 2. Redistributions in binary form must reproduce the above copyright
1187866Ssheldonh *    notice, this list of conditions and the following disclaimer in the
1287866Ssheldonh *    documentation and/or other materials provided with the distribution.
1387866Ssheldonh * 3. All advertising materials mentioning features or use of this software
1487866Ssheldonh *    must display the following acknowledgement:
1587866Ssheldonh *    This product includes software developed by Boris Popov.
1687866Ssheldonh * 4. Neither the name of the author nor the names of any co-contributors
1787866Ssheldonh *    may be used to endorse or promote products derived from this software
1887866Ssheldonh *    without specific prior written permission.
1987866Ssheldonh *
2087866Ssheldonh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2187866Ssheldonh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2287866Ssheldonh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2387866Ssheldonh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2487866Ssheldonh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2587866Ssheldonh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2687866Ssheldonh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2787866Ssheldonh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2887866Ssheldonh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2987866Ssheldonh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3087866Ssheldonh * SUCH DAMAGE.
3187866Ssheldonh *
3295267Ssheldonh * $Id: ctx.c,v 1.24 2002/04/13 14:35:28 bp Exp $
33113396Stjr * $FreeBSD: stable/11/contrib/smbfs/lib/smb/ctx.c 335774 2018-06-28 20:33:12Z brooks $
3487866Ssheldonh */
3587866Ssheldonh#include <sys/param.h>
3687866Ssheldonh#include <sys/sysctl.h>
3787866Ssheldonh#include <sys/ioctl.h>
3887866Ssheldonh#include <sys/time.h>
3987866Ssheldonh#include <sys/mount.h>
4087866Ssheldonh#include <fcntl.h>
4187866Ssheldonh#include <ctype.h>
4287866Ssheldonh#include <errno.h>
4387866Ssheldonh#include <stdio.h>
4487866Ssheldonh#include <string.h>
4587866Ssheldonh#include <stdlib.h>
4687866Ssheldonh#include <pwd.h>
4787866Ssheldonh#include <grp.h>
4887866Ssheldonh#include <unistd.h>
4987866Ssheldonh#include <sys/iconv.h>
5087866Ssheldonh
5187866Ssheldonh#define NB_NEEDRESOLVER
5287866Ssheldonh
5387866Ssheldonh#include <netsmb/smb_lib.h>
5487866Ssheldonh#include <netsmb/netbios.h>
5587866Ssheldonh#include <netsmb/nb_lib.h>
5687866Ssheldonh#include <netsmb/smb_conn.h>
5787866Ssheldonh#include <cflib.h>
5887866Ssheldonh
5987866Ssheldonh/*
6087866Ssheldonh * Prescan command line for [-U user] argument
6187866Ssheldonh * and fill context with defaults
6287866Ssheldonh */
6387866Ssheldonhint
6487866Ssheldonhsmb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
6587866Ssheldonh	int minlevel, int maxlevel, int sharetype)
6687866Ssheldonh{
6787866Ssheldonh	int  opt, error = 0;
6895267Ssheldonh	uid_t euid;
6987866Ssheldonh	const char *arg, *cp;
7095267Ssheldonh	struct passwd *pwd;
7187866Ssheldonh
7287866Ssheldonh	bzero(ctx,sizeof(*ctx));
7387866Ssheldonh	error = nb_ctx_create(&ctx->ct_nb);
7487866Ssheldonh	if (error)
7587866Ssheldonh		return error;
7687866Ssheldonh	ctx->ct_fd = -1;
7787866Ssheldonh	ctx->ct_parsedlevel = SMBL_NONE;
7887866Ssheldonh	ctx->ct_minlevel = minlevel;
7987866Ssheldonh	ctx->ct_maxlevel = maxlevel;
80150802Sbp	ctx->ct_smbtcpport = SMB_TCP_PORT;
8187866Ssheldonh
8287866Ssheldonh	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE;
8387866Ssheldonh	ctx->ct_ssn.ioc_timeout = 15;
8487866Ssheldonh	ctx->ct_ssn.ioc_retrycount = 4;
8587866Ssheldonh	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
8687866Ssheldonh	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
8787866Ssheldonh	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
8887866Ssheldonh	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
8987866Ssheldonh
9087866Ssheldonh	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
9187866Ssheldonh	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
9287866Ssheldonh	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
9387866Ssheldonh	ctx->ct_sh.ioc_mode = SMBM_EXEC;
9487866Ssheldonh	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
9587866Ssheldonh	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
9687866Ssheldonh	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
9787866Ssheldonh
9887866Ssheldonh	nb_ctx_setscope(ctx->ct_nb, "");
9995267Ssheldonh	euid = geteuid();
10095267Ssheldonh	if ((pwd = getpwuid(euid)) != NULL) {
10195267Ssheldonh		smb_ctx_setuser(ctx, pwd->pw_name);
10295267Ssheldonh		endpwent();
10395267Ssheldonh	} else if (euid == 0)
10495267Ssheldonh		smb_ctx_setuser(ctx, "root");
10595267Ssheldonh	else
10695267Ssheldonh		return 0;
10787866Ssheldonh	if (argv == NULL)
10887866Ssheldonh		return 0;
10987866Ssheldonh	for (opt = 1; opt < argc; opt++) {
11087866Ssheldonh		cp = argv[opt];
11187866Ssheldonh		if (strncmp(cp, "//", 2) != 0)
11287866Ssheldonh			continue;
11387866Ssheldonh		error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp);
11487866Ssheldonh		if (error)
11587866Ssheldonh			return error;
11687866Ssheldonh		ctx->ct_uncnext = cp;
11787866Ssheldonh		break;
11887866Ssheldonh	}
11987866Ssheldonh	while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) {
12087866Ssheldonh		arg = cf_optarg;
12187866Ssheldonh		switch (opt) {
12287866Ssheldonh		    case 'E':
12387866Ssheldonh			error = smb_ctx_setcharset(ctx, arg);
12487866Ssheldonh			if (error)
12587866Ssheldonh				return error;
12687866Ssheldonh			break;
12787866Ssheldonh		    case 'L':
128148815Simura			error = nls_setlocale(arg);
12987866Ssheldonh			if (error)
13087866Ssheldonh				break;
13187866Ssheldonh			break;
13287866Ssheldonh		    case 'U':
13387866Ssheldonh			error = smb_ctx_setuser(ctx, arg);
13487866Ssheldonh			break;
13587866Ssheldonh		}
13687866Ssheldonh	}
13787866Ssheldonh	cf_optind = cf_optreset = 1;
13887866Ssheldonh	return error;
13987866Ssheldonh}
14087866Ssheldonh
14187866Ssheldonhvoid
14287866Ssheldonhsmb_ctx_done(struct smb_ctx *ctx)
14387866Ssheldonh{
14487866Ssheldonh	if (ctx->ct_ssn.ioc_server)
14587866Ssheldonh		nb_snbfree(ctx->ct_ssn.ioc_server);
14687866Ssheldonh	if (ctx->ct_ssn.ioc_local)
14787866Ssheldonh		nb_snbfree(ctx->ct_ssn.ioc_local);
14887866Ssheldonh	if (ctx->ct_srvaddr)
14987866Ssheldonh		free(ctx->ct_srvaddr);
15087866Ssheldonh	if (ctx->ct_nb)
15187866Ssheldonh		nb_ctx_done(ctx->ct_nb);
15287866Ssheldonh}
15387866Ssheldonh
15487866Ssheldonhstatic int
15587866Ssheldonhgetsubstring(const char *p, u_char sep, char *dest, int maxlen, const char **next)
15687866Ssheldonh{
15787866Ssheldonh	int len;
15887866Ssheldonh
15987866Ssheldonh	maxlen--;
16087866Ssheldonh	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
16187866Ssheldonh		if (*p == 0)
16287866Ssheldonh			return EINVAL;
16387866Ssheldonh		*dest = *p;
16487866Ssheldonh	}
16587866Ssheldonh	*dest = 0;
16687866Ssheldonh	*next = *p ? p + 1 : p;
16787866Ssheldonh	return 0;
16887866Ssheldonh}
16987866Ssheldonh
17087866Ssheldonh/*
171150802Sbp * Here we expect something like "[proto:]//[user@]host[:psmb[:pnb]][/share][/path]"
17287866Ssheldonh */
17387866Ssheldonhint
17487866Ssheldonhsmb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
17587866Ssheldonh	const char **next)
17687866Ssheldonh{
17787866Ssheldonh	const char *p = unc;
178150802Sbp	char *p1, *psmb, *pnb;
17987866Ssheldonh	char tmp[1024];
18087866Ssheldonh	int error ;
18187866Ssheldonh
18287866Ssheldonh	ctx->ct_parsedlevel = SMBL_NONE;
18387866Ssheldonh	if (*p++ != '/' || *p++ != '/') {
18487866Ssheldonh		smb_error("UNC should start with '//'", 0);
18587866Ssheldonh		return EINVAL;
18687866Ssheldonh	}
18787866Ssheldonh	p1 = tmp;
18887866Ssheldonh	error = getsubstring(p, '@', p1, sizeof(tmp), &p);
18987866Ssheldonh	if (!error) {
19087866Ssheldonh		if (ctx->ct_maxlevel < SMBL_VC) {
19187866Ssheldonh			smb_error("no user name required", 0);
19287866Ssheldonh			return EINVAL;
19387866Ssheldonh		}
19487866Ssheldonh		error = smb_ctx_setuser(ctx, tmp);
19587866Ssheldonh		if (error)
19687866Ssheldonh			return error;
19787866Ssheldonh		ctx->ct_parsedlevel = SMBL_VC;
19887866Ssheldonh	}
19987866Ssheldonh	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
20087866Ssheldonh	if (error) {
20187866Ssheldonh		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
20287866Ssheldonh		if (error) {
20387866Ssheldonh			smb_error("no server name found", 0);
20487866Ssheldonh			return error;
20587866Ssheldonh		}
20687866Ssheldonh	}
20787866Ssheldonh	if (*p1 == 0) {
20887866Ssheldonh		smb_error("empty server name", 0);
20987866Ssheldonh		return EINVAL;
21087866Ssheldonh	}
211150802Sbp	/*
212150802Sbp	 * Check for port number specification.
213150802Sbp	 */
214150802Sbp	psmb = strchr(tmp, ':');
215150802Sbp	if (psmb) {
216150802Sbp		*psmb++ = '\0';
217150802Sbp		pnb = strchr(psmb, ':');
218150802Sbp		if (pnb) {
219150802Sbp			*pnb++ = '\0';
220150802Sbp			error = smb_ctx_setnbport(ctx, atoi(pnb));
221150802Sbp			if (error) {
222150802Sbp				smb_error("Invalid NetBIOS port number", 0);
223150802Sbp				return error;
224150802Sbp			}
225150802Sbp		}
226150802Sbp		error = smb_ctx_setsmbport(ctx, atoi(psmb));
227150802Sbp		if (error) {
228150802Sbp			smb_error("Invalid SMB port number", 0);
229150802Sbp			return error;
230150802Sbp		}
231150802Sbp	}
23287866Ssheldonh	error = smb_ctx_setserver(ctx, tmp);
23387866Ssheldonh	if (error)
23487866Ssheldonh		return error;
23587866Ssheldonh	if (sharetype == SMB_ST_NONE) {
23687866Ssheldonh		*next = p;
23787866Ssheldonh		return 0;
23887866Ssheldonh	}
23987866Ssheldonh	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
24087866Ssheldonh		smb_error("no share name required", 0);
24187866Ssheldonh		return EINVAL;
24287866Ssheldonh	}
24387866Ssheldonh	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
24487866Ssheldonh	if (error) {
24587866Ssheldonh		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
24687866Ssheldonh		if (error) {
24787866Ssheldonh			smb_error("unexpected end of line", 0);
24887866Ssheldonh			return error;
24987866Ssheldonh		}
25087866Ssheldonh	}
25187866Ssheldonh	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) {
25287866Ssheldonh		smb_error("empty share name", 0);
25387866Ssheldonh		return EINVAL;
25487866Ssheldonh	}
25587866Ssheldonh	*next = p;
25687866Ssheldonh	if (*p1 == 0)
25787866Ssheldonh		return 0;
25887866Ssheldonh	error = smb_ctx_setshare(ctx, p1, sharetype);
25987866Ssheldonh	return error;
26087866Ssheldonh}
26187866Ssheldonh
26287866Ssheldonhint
26387866Ssheldonhsmb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
26487866Ssheldonh{
26587866Ssheldonh	char *cp, *servercs, *localcs;
26687866Ssheldonh	int cslen = sizeof(ctx->ct_ssn.ioc_localcs);
26787866Ssheldonh	int scslen, lcslen, error;
26887866Ssheldonh
26987866Ssheldonh	cp = strchr(arg, ':');
27087866Ssheldonh	lcslen = cp ? (cp - arg) : 0;
27187866Ssheldonh	if (lcslen == 0 || lcslen >= cslen) {
27287866Ssheldonh		smb_error("invalid local charset specification (%s)", 0, arg);
27387866Ssheldonh		return EINVAL;
27487866Ssheldonh	}
27587866Ssheldonh	scslen = (size_t)strlen(++cp);
27687866Ssheldonh	if (scslen == 0 || scslen >= cslen) {
27787866Ssheldonh		smb_error("invalid server charset specification (%s)", 0, arg);
27887866Ssheldonh		return EINVAL;
27987866Ssheldonh	}
28087866Ssheldonh	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
28187866Ssheldonh	localcs[lcslen] = 0;
28287866Ssheldonh	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
28387866Ssheldonh	error = nls_setrecode(localcs, servercs);
28487866Ssheldonh	if (error == 0)
28587866Ssheldonh		return 0;
28687866Ssheldonh	smb_error("can't initialize iconv support (%s:%s)",
28787866Ssheldonh	    error, localcs, servercs);
28887866Ssheldonh	localcs[0] = 0;
28987866Ssheldonh	servercs[0] = 0;
29087866Ssheldonh	return error;
29187866Ssheldonh}
29287866Ssheldonh
29387866Ssheldonhint
29487866Ssheldonhsmb_ctx_setserver(struct smb_ctx *ctx, const char *name)
29587866Ssheldonh{
296118079Stjr	if (strlen(name) > SMB_MAXSRVNAMELEN) {
29787866Ssheldonh		smb_error("server name '%s' too long", 0, name);
29887866Ssheldonh		return ENAMETOOLONG;
29987866Ssheldonh	}
30087866Ssheldonh	nls_str_upper(ctx->ct_ssn.ioc_srvname, name);
30187866Ssheldonh	return 0;
30287866Ssheldonh}
30387866Ssheldonh
30487866Ssheldonhint
305150802Sbpsmb_ctx_setnbport(struct smb_ctx *ctx, int port)
306150802Sbp{
307150802Sbp	if (port < 1 || port > 0xffff)
308150802Sbp		return EINVAL;
309150802Sbp	ctx->ct_nb->nb_nmbtcpport = port;
310150802Sbp	return 0;
311150802Sbp}
312150802Sbp
313150802Sbpint
314150802Sbpsmb_ctx_setsmbport(struct smb_ctx *ctx, int port)
315150802Sbp{
316150802Sbp	if (port < 1 || port > 0xffff)
317150802Sbp		return EINVAL;
318150802Sbp	ctx->ct_smbtcpport = port;
319150802Sbp	ctx->ct_nb->nb_smbtcpport = port;
320150802Sbp	return 0;
321150802Sbp}
322150802Sbp
323150802Sbpint
32487866Ssheldonhsmb_ctx_setuser(struct smb_ctx *ctx, const char *name)
32587866Ssheldonh{
326118079Stjr	if (strlen(name) > SMB_MAXUSERNAMELEN) {
32787866Ssheldonh		smb_error("user name '%s' too long", 0, name);
32887866Ssheldonh		return ENAMETOOLONG;
32987866Ssheldonh	}
33087866Ssheldonh	nls_str_upper(ctx->ct_ssn.ioc_user, name);
33187866Ssheldonh	return 0;
33287866Ssheldonh}
33387866Ssheldonh
33487866Ssheldonhint
33587866Ssheldonhsmb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name)
33687866Ssheldonh{
337118079Stjr	if (strlen(name) > SMB_MAXUSERNAMELEN) {
33887866Ssheldonh		smb_error("workgroup name '%s' too long", 0, name);
33987866Ssheldonh		return ENAMETOOLONG;
34087866Ssheldonh	}
34187866Ssheldonh	nls_str_upper(ctx->ct_ssn.ioc_workgroup, name);
34287866Ssheldonh	return 0;
34387866Ssheldonh}
34487866Ssheldonh
34587866Ssheldonhint
34687866Ssheldonhsmb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd)
34787866Ssheldonh{
34887866Ssheldonh	if (passwd == NULL)
34987866Ssheldonh		return EINVAL;
350118079Stjr	if (strlen(passwd) > SMB_MAXPASSWORDLEN) {
35187866Ssheldonh		smb_error("password too long", 0);
35287866Ssheldonh		return ENAMETOOLONG;
35387866Ssheldonh	}
35487866Ssheldonh	if (strncmp(passwd, "$$1", 3) == 0)
35587866Ssheldonh		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
35687866Ssheldonh	else
35787866Ssheldonh		strcpy(ctx->ct_ssn.ioc_password, passwd);
35887866Ssheldonh	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
35987866Ssheldonh	return 0;
36087866Ssheldonh}
36187866Ssheldonh
36287866Ssheldonhint
36387866Ssheldonhsmb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
36487866Ssheldonh{
365118079Stjr	if (strlen(share) > SMB_MAXSHARENAMELEN) {
36687866Ssheldonh		smb_error("share name '%s' too long", 0, share);
36787866Ssheldonh		return ENAMETOOLONG;
36887866Ssheldonh	}
36987866Ssheldonh	nls_str_upper(ctx->ct_sh.ioc_share, share);
37087866Ssheldonh	if (share[0] != 0)
37187866Ssheldonh		ctx->ct_parsedlevel = SMBL_SHARE;
37287866Ssheldonh	ctx->ct_sh.ioc_stype = stype;
37387866Ssheldonh	return 0;
37487866Ssheldonh}
37587866Ssheldonh
37687866Ssheldonhint
37787866Ssheldonhsmb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
37887866Ssheldonh{
37987866Ssheldonh	if (addr == NULL || addr[0] == 0)
38087866Ssheldonh		return EINVAL;
38187866Ssheldonh	if (ctx->ct_srvaddr)
38287866Ssheldonh		free(ctx->ct_srvaddr);
38387866Ssheldonh	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
38487866Ssheldonh		return ENOMEM;
38587866Ssheldonh	return 0;
38687866Ssheldonh}
38787866Ssheldonh
38887866Ssheldonhstatic int
38987866Ssheldonhsmb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
39087866Ssheldonh{
39187866Ssheldonh	struct group *gr;
39287866Ssheldonh	struct passwd *pw;
39387866Ssheldonh	char *cp;
39487866Ssheldonh
39587866Ssheldonh	cp = strchr(pair, ':');
39687866Ssheldonh	if (cp) {
39787866Ssheldonh		*cp++ = '\0';
39887866Ssheldonh		if (*cp) {
39987866Ssheldonh			gr = getgrnam(cp);
40087866Ssheldonh			if (gr) {
40187866Ssheldonh				*gid = gr->gr_gid;
40287866Ssheldonh			} else
40387866Ssheldonh				smb_error("Invalid group name %s, ignored",
40487866Ssheldonh				    0, cp);
40587866Ssheldonh		}
40687866Ssheldonh	}
40787866Ssheldonh	if (*pair) {
40887866Ssheldonh		pw = getpwnam(pair);
40987866Ssheldonh		if (pw) {
41087866Ssheldonh			*uid = pw->pw_uid;
41187866Ssheldonh		} else
41287866Ssheldonh			smb_error("Invalid user name %s, ignored", 0, pair);
41387866Ssheldonh	}
41487866Ssheldonh	endpwent();
41587866Ssheldonh	return 0;
41687866Ssheldonh}
41787866Ssheldonh
41887866Ssheldonhint
41987866Ssheldonhsmb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
42087866Ssheldonh{
42187866Ssheldonh	int error = 0;
42287866Ssheldonh	char *p, *cp;
42387866Ssheldonh
42487866Ssheldonh	switch(opt) {
42587866Ssheldonh	    case 'U':
42687866Ssheldonh		break;
42787866Ssheldonh	    case 'I':
42887866Ssheldonh		error = smb_ctx_setsrvaddr(ctx, arg);
42987866Ssheldonh		break;
43087866Ssheldonh	    case 'M':
43187866Ssheldonh		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
43287866Ssheldonh		if (*cp == '/') {
43387866Ssheldonh			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
43487866Ssheldonh			ctx->ct_flags |= SMBCF_SRIGHTS;
43587866Ssheldonh		}
43687866Ssheldonh		break;
43787866Ssheldonh	    case 'N':
43887866Ssheldonh		ctx->ct_flags |= SMBCF_NOPWD;
43987866Ssheldonh		break;
44087866Ssheldonh	    case 'O':
44187866Ssheldonh		p = strdup(arg);
44287866Ssheldonh		cp = strchr(p, '/');
44387866Ssheldonh		if (cp) {
44487866Ssheldonh			*cp++ = '\0';
44587866Ssheldonh			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
44687866Ssheldonh			    &ctx->ct_sh.ioc_group);
44787866Ssheldonh		}
44887866Ssheldonh		if (*p && error == 0) {
449113396Stjr			error = smb_parse_owner(p, &ctx->ct_ssn.ioc_owner,
45087866Ssheldonh			    &ctx->ct_ssn.ioc_group);
45187866Ssheldonh		}
45287866Ssheldonh		free(p);
45387866Ssheldonh		break;
45487866Ssheldonh	    case 'P':
45587866Ssheldonh/*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT;*/
45687866Ssheldonh		break;
45787866Ssheldonh	    case 'R':
45887866Ssheldonh		ctx->ct_ssn.ioc_retrycount = atoi(arg);
45987866Ssheldonh		break;
46087866Ssheldonh	    case 'T':
46187866Ssheldonh		ctx->ct_ssn.ioc_timeout = atoi(arg);
46287866Ssheldonh		break;
46387866Ssheldonh	    case 'W':
46487866Ssheldonh		error = smb_ctx_setworkgroup(ctx, arg);
46587866Ssheldonh		break;
46687866Ssheldonh	}
46787866Ssheldonh	return error;
46887866Ssheldonh}
46987866Ssheldonh
47087866Ssheldonh#if 0
47187866Ssheldonhstatic void
47287866Ssheldonhsmb_hexdump(const u_char *buf, int len) {
47387866Ssheldonh	int ofs = 0;
47487866Ssheldonh
47587866Ssheldonh	while (len--) {
47687866Ssheldonh		if (ofs % 16 == 0)
47787866Ssheldonh			printf("\n%02X: ", ofs);
47887866Ssheldonh		printf("%02x ", *buf++);
47987866Ssheldonh		ofs++;
48087866Ssheldonh	}
48187866Ssheldonh	printf("\n");
48287866Ssheldonh}
48387866Ssheldonh#endif
48487866Ssheldonh
48587866Ssheldonh
48687866Ssheldonhstatic int
48787866Ssheldonhsmb_addiconvtbl(const char *to, const char *from, const u_char *tbl)
48887866Ssheldonh{
48987866Ssheldonh	int error;
49087866Ssheldonh
49187866Ssheldonh	error = kiconv_add_xlat_table(to, from, tbl);
49287866Ssheldonh	if (error && error != EEXIST) {
49387866Ssheldonh		smb_error("can not setup kernel iconv table (%s:%s)", error,
49487866Ssheldonh		    from, to);
49587866Ssheldonh		return error;
49687866Ssheldonh	}
49787866Ssheldonh	return 0;
49887866Ssheldonh}
49987866Ssheldonh
50087866Ssheldonh/*
50187866Ssheldonh * Verify context before connect operation(s),
50287866Ssheldonh * lookup specified server and try to fill all forgotten fields.
50387866Ssheldonh */
50487866Ssheldonhint
50587866Ssheldonhsmb_ctx_resolve(struct smb_ctx *ctx)
50687866Ssheldonh{
50787866Ssheldonh	struct smbioc_ossn *ssn = &ctx->ct_ssn;
50887866Ssheldonh	struct smbioc_oshare *sh = &ctx->ct_sh;
50987866Ssheldonh	struct nb_name nn;
51087866Ssheldonh	struct sockaddr *sap;
51187866Ssheldonh	struct sockaddr_nb *salocal, *saserver;
51287866Ssheldonh	char *cp;
51387866Ssheldonh	int error = 0;
51487866Ssheldonh
51587866Ssheldonh	ctx->ct_flags &= ~SMBCF_RESOLVED;
51687866Ssheldonh	if (ssn->ioc_srvname[0] == 0) {
51787866Ssheldonh		smb_error("no server name specified", 0);
51887866Ssheldonh		return EINVAL;
51987866Ssheldonh	}
52087866Ssheldonh	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0) {
52187866Ssheldonh		smb_error("no share name specified for %s@%s",
52287866Ssheldonh		    0, ssn->ioc_user, ssn->ioc_srvname);
52387866Ssheldonh		return EINVAL;
52487866Ssheldonh	}
52587866Ssheldonh	error = nb_ctx_resolve(ctx->ct_nb);
52687866Ssheldonh	if (error)
52787866Ssheldonh		return error;
52887866Ssheldonh	if (ssn->ioc_localcs[0] == 0)
529145872Stakawata		strcpy(ssn->ioc_localcs, "ISO8859-1");
53087866Ssheldonh	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
53187866Ssheldonh	if (error)
53287866Ssheldonh		return error;
53387866Ssheldonh	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
53487866Ssheldonh	if (error)
53587866Ssheldonh		return error;
53687866Ssheldonh	if (ssn->ioc_servercs[0] != 0) {
537145872Stakawata		error = kiconv_add_xlat16_cspairs
538148519Simura			(ssn->ioc_servercs, ssn->ioc_localcs);
539145872Stakawata		if (error) return error;
54087866Ssheldonh	}
54187866Ssheldonh	if (ctx->ct_srvaddr) {
542150802Sbp		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap, ctx->ct_smbtcpport);
54387866Ssheldonh	} else {
54487866Ssheldonh		error = nbns_resolvename(ssn->ioc_srvname, ctx->ct_nb, &sap);
54587866Ssheldonh	}
54687866Ssheldonh	if (error) {
54787866Ssheldonh		smb_error("can't get server address", error);
54887866Ssheldonh		return error;
54987866Ssheldonh	}
55087866Ssheldonh	nn.nn_scope = ctx->ct_nb->nb_scope;
55187866Ssheldonh	nn.nn_type = NBT_SERVER;
552335774Sbrooks	if (strlen(ssn->ioc_srvname) > NB_NAMELEN)
553335774Sbrooks		return NBERROR(NBERR_NAMETOOLONG);
554335774Sbrooks	strlcpy(nn.nn_name, ssn->ioc_srvname, sizeof(nn.nn_name));
55587866Ssheldonh	error = nb_sockaddr(sap, &nn, &saserver);
55687866Ssheldonh	nb_snbfree(sap);
55787866Ssheldonh	if (error) {
55887866Ssheldonh		smb_error("can't allocate server address", error);
55987866Ssheldonh		return error;
56087866Ssheldonh	}
56187866Ssheldonh	ssn->ioc_server = (struct sockaddr*)saserver;
56287866Ssheldonh	if (ctx->ct_locname[0] == 0) {
56387866Ssheldonh		error = nb_getlocalname(ctx->ct_locname);
56487866Ssheldonh		if (error) {
56587866Ssheldonh			smb_error("can't get local name", error);
56687866Ssheldonh			return error;
56787866Ssheldonh		}
56887866Ssheldonh		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
56987866Ssheldonh	}
570335774Sbrooks	/*
571335774Sbrooks	 * Truncate the local host name to NB_NAMELEN-1 which gives a
572335774Sbrooks	 * suffix of 0 which is "workstation name".
573335774Sbrooks	 */
574335774Sbrooks	strlcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN);
57587866Ssheldonh	nn.nn_type = NBT_WKSTA;
57687866Ssheldonh	nn.nn_scope = ctx->ct_nb->nb_scope;
57787866Ssheldonh	error = nb_sockaddr(NULL, &nn, &salocal);
57887866Ssheldonh	if (error) {
57987866Ssheldonh		nb_snbfree((struct sockaddr*)saserver);
58087866Ssheldonh		smb_error("can't allocate local address", error);
58187866Ssheldonh		return error;
58287866Ssheldonh	}
58387866Ssheldonh	ssn->ioc_local = (struct sockaddr*)salocal;
58487866Ssheldonh	ssn->ioc_lolen = salocal->snb_len;
58587866Ssheldonh	ssn->ioc_svlen = saserver->snb_len;
58687866Ssheldonh	if (ssn->ioc_password[0] == 0 && (ctx->ct_flags & SMBCF_NOPWD) == 0) {
58787866Ssheldonh		cp = getpass("Password:");
58887866Ssheldonh		error = smb_ctx_setpassword(ctx, cp);
58987866Ssheldonh		if (error)
59087866Ssheldonh			return error;
59187866Ssheldonh	}
59287866Ssheldonh	ctx->ct_flags |= SMBCF_RESOLVED;
59387866Ssheldonh	return 0;
59487866Ssheldonh}
59587866Ssheldonh
59687866Ssheldonhstatic int
59787866Ssheldonhsmb_ctx_gethandle(struct smb_ctx *ctx)
59887866Ssheldonh{
59987866Ssheldonh	int fd, i;
60087866Ssheldonh	char buf[20];
60187866Ssheldonh
60287866Ssheldonh	fd = open("/dev/"NSMB_NAME, O_RDWR);
60387866Ssheldonh	if (fd >= 0) {
60487866Ssheldonh		ctx->ct_fd = fd;
60587866Ssheldonh		return 0;
60687866Ssheldonh	}
607250236Sdavide	return ENOENT;
60887866Ssheldonh}
60987866Ssheldonh
61087866Ssheldonhint
61187866Ssheldonhsmb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
61287866Ssheldonh{
61387866Ssheldonh	struct smbioc_lookup rq;
61487866Ssheldonh	int error;
61587866Ssheldonh
61687866Ssheldonh	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
61787866Ssheldonh		smb_error("smb_ctx_lookup() data is not resolved", 0);
61887866Ssheldonh		return EINVAL;
61987866Ssheldonh	}
62087866Ssheldonh	if (ctx->ct_fd != -1) {
62187866Ssheldonh		close(ctx->ct_fd);
62287866Ssheldonh		ctx->ct_fd = -1;
62387866Ssheldonh	}
62487866Ssheldonh	error = smb_ctx_gethandle(ctx);
62587866Ssheldonh	if (error) {
62688492Ssheldonh		smb_error("can't get handle to requester (no /dev/"NSMB_NAME"* device)", 0);
62787866Ssheldonh		return EINVAL;
62887866Ssheldonh	}
62987866Ssheldonh	bzero(&rq, sizeof(rq));
63087866Ssheldonh	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof(struct smbioc_ossn));
63187866Ssheldonh	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof(struct smbioc_oshare));
63287866Ssheldonh	rq.ioc_flags = flags;
63387866Ssheldonh	rq.ioc_level = level;
63487866Ssheldonh	if (ioctl(ctx->ct_fd, SMBIOC_LOOKUP, &rq) == -1) {
63587866Ssheldonh		error = errno;
63687866Ssheldonh		if (flags & SMBLK_CREATE)
63787866Ssheldonh			smb_error("unable to open connection", error);
63887866Ssheldonh		return error;
63987866Ssheldonh	}
64087866Ssheldonh	return 0;
64187866Ssheldonh}
64287866Ssheldonh
64387866Ssheldonhint
64487866Ssheldonhsmb_ctx_login(struct smb_ctx *ctx)
64587866Ssheldonh{
64687866Ssheldonh	struct smbioc_ossn *ssn = &ctx->ct_ssn;
64787866Ssheldonh	struct smbioc_oshare *sh = &ctx->ct_sh;
64887866Ssheldonh	int error;
64987866Ssheldonh
65087866Ssheldonh	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
65187866Ssheldonh		smb_error("smb_ctx_resolve() should be called first", 0);
65287866Ssheldonh		return EINVAL;
65387866Ssheldonh	}
65487866Ssheldonh	if (ctx->ct_fd != -1) {
65587866Ssheldonh		close(ctx->ct_fd);
65687866Ssheldonh		ctx->ct_fd = -1;
65787866Ssheldonh	}
65887866Ssheldonh	error = smb_ctx_gethandle(ctx);
65987866Ssheldonh	if (error) {
66087866Ssheldonh		smb_error("can't get handle to requester", 0);
66187866Ssheldonh		return EINVAL;
66287866Ssheldonh	}
66387866Ssheldonh	if (ioctl(ctx->ct_fd, SMBIOC_OPENSESSION, ssn) == -1) {
66487866Ssheldonh		error = errno;
66587866Ssheldonh		smb_error("can't open session to server %s", error, ssn->ioc_srvname);
66687866Ssheldonh		return error;
66787866Ssheldonh	}
66887866Ssheldonh	if (sh->ioc_share[0] == 0)
66987866Ssheldonh		return 0;
67087866Ssheldonh	if (ioctl(ctx->ct_fd, SMBIOC_OPENSHARE, sh) == -1) {
67187866Ssheldonh		error = errno;
67287866Ssheldonh		smb_error("can't connect to share //%s/%s", error,
67387866Ssheldonh		    ssn->ioc_srvname, sh->ioc_share);
67487866Ssheldonh		return error;
67587866Ssheldonh	}
67687866Ssheldonh	return 0;
67787866Ssheldonh}
67887866Ssheldonh
67987866Ssheldonhint
68087866Ssheldonhsmb_ctx_setflags(struct smb_ctx *ctx, int level, int mask, int flags)
68187866Ssheldonh{
68287866Ssheldonh	struct smbioc_flags fl;
68387866Ssheldonh
68487866Ssheldonh	if (ctx->ct_fd == -1)
68587866Ssheldonh		return EINVAL;
68687866Ssheldonh	fl.ioc_level = level;
68787866Ssheldonh	fl.ioc_mask = mask;
68887866Ssheldonh	fl.ioc_flags = flags;
68987866Ssheldonh	if (ioctl(ctx->ct_fd, SMBIOC_SETFLAGS, &fl) == -1)
69087866Ssheldonh		return errno;
69187866Ssheldonh	return 0;
69287866Ssheldonh}
69387866Ssheldonh
69487866Ssheldonh/*
69587866Ssheldonh * level values:
69687866Ssheldonh * 0 - default
69787866Ssheldonh * 1 - server
69887866Ssheldonh * 2 - server:user
69987866Ssheldonh * 3 - server:user:share
70087866Ssheldonh */
70187866Ssheldonhstatic int
70287866Ssheldonhsmb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
70387866Ssheldonh{
70487866Ssheldonh	char *p;
70587866Ssheldonh	int error;
70687866Ssheldonh
70788492Ssheldonh	if (level >= 0) {
70887866Ssheldonh		rc_getstringptr(smb_rc, sname, "charsets", &p);
70987866Ssheldonh		if (p) {
71087866Ssheldonh			error = smb_ctx_setcharset(ctx, p);
71187866Ssheldonh			if (error)
71287866Ssheldonh				smb_error("charset specification in the section '%s' ignored", error, sname);
71387866Ssheldonh		}
71487866Ssheldonh	}
71587866Ssheldonh	if (level <= 1) {
71687866Ssheldonh		rc_getint(smb_rc, sname, "timeout", &ctx->ct_ssn.ioc_timeout);
71787866Ssheldonh		rc_getint(smb_rc, sname, "retry_count", &ctx->ct_ssn.ioc_retrycount);
71887866Ssheldonh	}
71987866Ssheldonh	if (level == 1) {
72087866Ssheldonh		rc_getstringptr(smb_rc, sname, "addr", &p);
72187866Ssheldonh		if (p) {
72287866Ssheldonh			error = smb_ctx_setsrvaddr(ctx, p);
72387866Ssheldonh			if (error) {
72487866Ssheldonh				smb_error("invalid address specified in the section %s", 0, sname);
72587866Ssheldonh				return error;
72687866Ssheldonh			}
72787866Ssheldonh		}
72887866Ssheldonh	}
72987866Ssheldonh	if (level >= 2) {
73087866Ssheldonh		rc_getstringptr(smb_rc, sname, "password", &p);
73187866Ssheldonh		if (p)
73287866Ssheldonh			smb_ctx_setpassword(ctx, p);
73387866Ssheldonh	}
73487866Ssheldonh	rc_getstringptr(smb_rc, sname, "workgroup", &p);
73587866Ssheldonh	if (p)
73687866Ssheldonh		smb_ctx_setworkgroup(ctx, p);
73787866Ssheldonh	return 0;
73887866Ssheldonh}
73987866Ssheldonh
74087866Ssheldonh/*
74187866Ssheldonh * read rc file as follows:
74287866Ssheldonh * 1. read [default] section
74387866Ssheldonh * 2. override with [server] section
74487866Ssheldonh * 3. override with [server:user:share] section
74587866Ssheldonh * Since abcence of rcfile is not fatal, silently ignore this fact.
74687866Ssheldonh * smb_rc file should be closed by caller.
74787866Ssheldonh */
74887866Ssheldonhint
74987866Ssheldonhsmb_ctx_readrc(struct smb_ctx *ctx)
75087866Ssheldonh{
75187866Ssheldonh	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + SMB_MAXSHARENAMELEN + 4];
75287866Ssheldonh/*	char *p;*/
75387866Ssheldonh
75487866Ssheldonh	if (smb_open_rcfile() != 0)
75587866Ssheldonh		return 0;
75687866Ssheldonh
75787866Ssheldonh	if (ctx->ct_ssn.ioc_user[0] == 0 || ctx->ct_ssn.ioc_srvname[0] == 0)
75887866Ssheldonh		return 0;
75987866Ssheldonh
76087866Ssheldonh	smb_ctx_readrcsection(ctx, "default", 0);
76187866Ssheldonh	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
76287866Ssheldonh	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
76387866Ssheldonh	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, ctx->ct_ssn.ioc_srvname, 1);
76487866Ssheldonh	/*
76587866Ssheldonh	 * SERVER:USER parameters
76687866Ssheldonh	 */
76787866Ssheldonh	snprintf(sname, sizeof(sname), "%s:%s", ctx->ct_ssn.ioc_srvname,
76887866Ssheldonh	    ctx->ct_ssn.ioc_user);
76987866Ssheldonh	smb_ctx_readrcsection(ctx, sname, 2);
77087866Ssheldonh
77187866Ssheldonh	if (ctx->ct_sh.ioc_share[0] != 0) {
77287866Ssheldonh		/*
77387866Ssheldonh		 * SERVER:USER:SHARE parameters
77487866Ssheldonh	         */
77587866Ssheldonh		snprintf(sname, sizeof(sname), "%s:%s:%s", ctx->ct_ssn.ioc_srvname,
77687866Ssheldonh		    ctx->ct_ssn.ioc_user, ctx->ct_sh.ioc_share);
77787866Ssheldonh		smb_ctx_readrcsection(ctx, sname, 3);
77887866Ssheldonh	}
77987866Ssheldonh	return 0;
78087866Ssheldonh}
78187866Ssheldonh
782