ctx.c revision 335781
159191Skris/*
259191Skris * Copyright (c) 2000-2002, Boris Popov
359191Skris * All rights reserved.
459191Skris *
559191Skris * Redistribution and use in source and binary forms, with or without
659191Skris * modification, are permitted provided that the following conditions
759191Skris * are met:
8280304Sjkim * 1. Redistributions of source code must retain the above copyright
959191Skris *    notice, this list of conditions and the following disclaimer.
1059191Skris * 2. Redistributions in binary form must reproduce the above copyright
1159191Skris *    notice, this list of conditions and the following disclaimer in the
1259191Skris *    documentation and/or other materials provided with the distribution.
1359191Skris * 3. All advertising materials mentioning features or use of this software
1459191Skris *    must display the following acknowledgement:
15280304Sjkim *    This product includes software developed by Boris Popov.
1659191Skris * 4. Neither the name of the author nor the names of any co-contributors
1759191Skris *    may be used to endorse or promote products derived from this software
1859191Skris *    without specific prior written permission.
1959191Skris *
2059191Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2159191Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22280304Sjkim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359191Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2459191Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559191Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659191Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759191Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859191Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959191Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059191Skris * SUCH DAMAGE.
3159191Skris *
3259191Skris * $Id: ctx.c,v 1.24 2002/04/13 14:35:28 bp Exp $
3359191Skris * $FreeBSD: stable/10/contrib/smbfs/lib/smb/ctx.c 335781 2018-06-28 21:23:05Z brooks $
3459191Skris */
3559191Skris#include <sys/param.h>
3659191Skris#include <sys/sysctl.h>
37280304Sjkim#include <sys/ioctl.h>
3859191Skris#include <sys/time.h>
3959191Skris#include <sys/mount.h>
40280304Sjkim#include <fcntl.h>
4159191Skris#include <ctype.h>
4259191Skris#include <errno.h>
4359191Skris#include <stdio.h>
4459191Skris#include <string.h>
4559191Skris#include <stdlib.h>
4659191Skris#include <pwd.h>
4759191Skris#include <grp.h>
4859191Skris#include <unistd.h>
4959191Skris#include <sys/iconv.h>
5059191Skris
5159191Skris#define NB_NEEDRESOLVER
52280304Sjkim
5359191Skris#include <netsmb/smb_lib.h>
5459191Skris#include <netsmb/netbios.h>
5559191Skris#include <netsmb/nb_lib.h>
5659191Skris#include <netsmb/smb_conn.h>
5759191Skris#include <cflib.h>
5859191Skris
5959191Skris/*
6059191Skris * Prescan command line for [-U user] argument
6159191Skris * and fill context with defaults
6259191Skris */
6359191Skrisint
64238405Sjkimsmb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
6559191Skris	int minlevel, int maxlevel, int sharetype)
6659191Skris{
6759191Skris	int  opt, error = 0;
6859191Skris	uid_t euid;
6959191Skris	const char *arg, *cp;
70280304Sjkim	struct passwd *pwd;
71280304Sjkim
72280304Sjkim	bzero(ctx,sizeof(*ctx));
73280304Sjkim	error = nb_ctx_create(&ctx->ct_nb);
7459191Skris	if (error)
7559191Skris		return error;
7659191Skris	ctx->ct_fd = -1;
7759191Skris	ctx->ct_parsedlevel = SMBL_NONE;
78280304Sjkim	ctx->ct_minlevel = minlevel;
79280304Sjkim	ctx->ct_maxlevel = maxlevel;
80280304Sjkim	ctx->ct_smbtcpport = SMB_TCP_PORT;
81280304Sjkim
82280304Sjkim	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE;
83280304Sjkim	ctx->ct_ssn.ioc_timeout = 15;
84280304Sjkim	ctx->ct_ssn.ioc_retrycount = 4;
85280304Sjkim	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
86280304Sjkim	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
87280304Sjkim	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
88280304Sjkim	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
89280304Sjkim
9059191Skris	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
9159191Skris	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
92280304Sjkim	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
93280304Sjkim	ctx->ct_sh.ioc_mode = SMBM_EXEC;
94160814Ssimon	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
95160814Ssimon	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
96280304Sjkim	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
97160814Ssimon
98160814Ssimon	nb_ctx_setscope(ctx->ct_nb, "");
99160814Ssimon	euid = geteuid();
100160814Ssimon	if ((pwd = getpwuid(euid)) != NULL) {
101160814Ssimon		smb_ctx_setuser(ctx, pwd->pw_name);
102160814Ssimon		endpwent();
103160814Ssimon	} else if (euid == 0)
104160814Ssimon		smb_ctx_setuser(ctx, "root");
105160814Ssimon	else
106160814Ssimon		return 0;
107280304Sjkim	if (argv == NULL)
108280304Sjkim		return 0;
109284285Sjkim	for (opt = 1; opt < argc; opt++) {
110280304Sjkim		cp = argv[opt];
111280304Sjkim		if (strncmp(cp, "//", 2) != 0)
112280304Sjkim			continue;
113280304Sjkim		error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp);
114280304Sjkim		if (error)
115284285Sjkim			return error;
116280304Sjkim		ctx->ct_uncnext = cp;
117160814Ssimon		break;
118280304Sjkim	}
119280304Sjkim	while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) {
120284285Sjkim		arg = cf_optarg;
121280304Sjkim		switch (opt) {
122280304Sjkim		    case 'E':
123280304Sjkim			error = smb_ctx_setcharset(ctx, arg);
124280304Sjkim			if (error)
125284285Sjkim				return error;
126280304Sjkim			break;
127160814Ssimon		    case 'L':
128109998Smarkm			error = nls_setlocale(arg);
12959191Skris			if (error)
130280304Sjkim				break;
13159191Skris			break;
13259191Skris		    case 'U':
13359191Skris			error = smb_ctx_setuser(ctx, arg);
134280304Sjkim			break;
135280304Sjkim		}
136280304Sjkim	}
137280304Sjkim	cf_optind = cf_optreset = 1;
138280304Sjkim	return error;
139280304Sjkim}
140280304Sjkim
141280304Sjkimvoid
14259191Skrissmb_ctx_done(struct smb_ctx *ctx)
143280304Sjkim{
144280304Sjkim	if (ctx->ct_ssn.ioc_server)
145111147Snectar		nb_snbfree(ctx->ct_ssn.ioc_server);
146280304Sjkim	if (ctx->ct_ssn.ioc_local)
147280304Sjkim		nb_snbfree(ctx->ct_ssn.ioc_local);
148280304Sjkim	if (ctx->ct_srvaddr)
149280304Sjkim		free(ctx->ct_srvaddr);
150111147Snectar	if (ctx->ct_nb)
151280304Sjkim		nb_ctx_done(ctx->ct_nb);
152280304Sjkim}
153280304Sjkim
154280304Sjkimstatic int
155280304Sjkimgetsubstring(const char *p, u_char sep, char *dest, int maxlen, const char **next)
156280304Sjkim{
157280304Sjkim	int len;
158280304Sjkim
159280304Sjkim	maxlen--;
160280304Sjkim	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
161280304Sjkim		if (*p == 0)
162280304Sjkim			return EINVAL;
163280304Sjkim		*dest = *p;
164280304Sjkim	}
165280304Sjkim	*dest = 0;
166280304Sjkim	*next = *p ? p + 1 : p;
167280304Sjkim	return 0;
16859191Skris}
169280304Sjkim
170280304Sjkim/*
171280304Sjkim * Here we expect something like "[proto:]//[user@]host[:psmb[:pnb]][/share][/path]"
172280304Sjkim */
173280304Sjkimint
174280304Sjkimsmb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
175280304Sjkim	const char **next)
176280304Sjkim{
177280304Sjkim	const char *p = unc;
17859191Skris	char *p1, *psmb, *pnb;
179280304Sjkim	char tmp[1024];
180280304Sjkim	int error ;
181280304Sjkim
182280304Sjkim	ctx->ct_parsedlevel = SMBL_NONE;
183280304Sjkim	if (*p++ != '/' || *p++ != '/') {
184280304Sjkim		smb_error("UNC should start with '//'", 0);
185280304Sjkim		return EINVAL;
186280304Sjkim	}
187280304Sjkim	p1 = tmp;
188280304Sjkim	error = getsubstring(p, '@', p1, sizeof(tmp), &p);
18959191Skris	if (!error) {
190280304Sjkim		if (ctx->ct_maxlevel < SMBL_VC) {
191280304Sjkim			smb_error("no user name required", 0);
192280304Sjkim			return EINVAL;
193280304Sjkim		}
194280304Sjkim		error = smb_ctx_setuser(ctx, tmp);
195280304Sjkim		if (error)
196280304Sjkim			return error;
197280304Sjkim		ctx->ct_parsedlevel = SMBL_VC;
198280304Sjkim	}
199280304Sjkim	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
200280304Sjkim	if (error) {
201295016Sjkim		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
202295016Sjkim		if (error) {
203295016Sjkim			smb_error("no server name found", 0);
204280304Sjkim			return error;
205280304Sjkim		}
20659191Skris	}
207280304Sjkim	if (*p1 == 0) {
208295016Sjkim		smb_error("empty server name", 0);
209280304Sjkim		return EINVAL;
210280304Sjkim	}
211280304Sjkim	/*
212280304Sjkim	 * Check for port number specification.
213280304Sjkim	 */
214280304Sjkim	psmb = strchr(tmp, ':');
215280304Sjkim	if (psmb) {
216280304Sjkim		*psmb++ = '\0';
217280304Sjkim		pnb = strchr(psmb, ':');
218280304Sjkim		if (pnb) {
219280304Sjkim			*pnb++ = '\0';
220280304Sjkim			error = smb_ctx_setnbport(ctx, atoi(pnb));
22159191Skris			if (error) {
222280304Sjkim				smb_error("Invalid NetBIOS port number", 0);
223280304Sjkim				return error;
224280304Sjkim			}
225280304Sjkim		}
226280304Sjkim		error = smb_ctx_setsmbport(ctx, atoi(psmb));
227280304Sjkim		if (error) {
228111147Snectar			smb_error("Invalid SMB port number", 0);
229280304Sjkim			return error;
230280304Sjkim		}
231280304Sjkim	}
232280304Sjkim	error = smb_ctx_setserver(ctx, tmp);
233111147Snectar	if (error)
234280304Sjkim		return error;
235280304Sjkim	if (sharetype == SMB_ST_NONE) {
23659191Skris		*next = p;
237280304Sjkim		return 0;
238280304Sjkim	}
239280304Sjkim	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
240280304Sjkim		smb_error("no share name required", 0);
241280304Sjkim		return EINVAL;
24259191Skris	}
243280304Sjkim	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
244280304Sjkim	if (error) {
24559191Skris		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
246280304Sjkim		if (error) {
247280304Sjkim			smb_error("unexpected end of line", 0);
248280304Sjkim			return error;
249280304Sjkim		}
250306196Sjkim	}
251306196Sjkim	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) {
252280304Sjkim		smb_error("empty share name", 0);
253280304Sjkim		return EINVAL;
254280304Sjkim	}
25559191Skris	*next = p;
256306196Sjkim	if (*p1 == 0)
257280304Sjkim		return 0;
258280304Sjkim	error = smb_ctx_setshare(ctx, p1, sharetype);
259280304Sjkim	return error;
260280304Sjkim}
261280304Sjkim
262160814Ssimonint
263280304Sjkimsmb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
264160814Ssimon{
265280304Sjkim	char *cp, *servercs, *localcs;
266280304Sjkim	int cslen = sizeof(ctx->ct_ssn.ioc_localcs);
267280304Sjkim	int scslen, lcslen, error;
268160814Ssimon
269306196Sjkim	cp = strchr(arg, ':');
270306196Sjkim	lcslen = cp ? (cp - arg) : 0;
271280304Sjkim	if (lcslen == 0 || lcslen >= cslen) {
272280304Sjkim		smb_error("invalid local charset specification (%s)", 0, arg);
273280304Sjkim		return EINVAL;
274280304Sjkim	}
275280304Sjkim	scslen = (size_t)strlen(++cp);
276280304Sjkim	if (scslen == 0 || scslen >= cslen) {
277160814Ssimon		smb_error("invalid server charset specification (%s)", 0, arg);
278280304Sjkim		return EINVAL;
279280304Sjkim	}
280280304Sjkim	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
281280304Sjkim	localcs[lcslen] = 0;
282280304Sjkim	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
283280304Sjkim	error = nls_setrecode(localcs, servercs);
28459191Skris	if (error == 0)
285280304Sjkim		return 0;
286280304Sjkim	smb_error("can't initialize iconv support (%s:%s)",
287280304Sjkim	    error, localcs, servercs);
288280304Sjkim	localcs[0] = 0;
289306196Sjkim	servercs[0] = 0;
290280304Sjkim	return error;
291280304Sjkim}
292280304Sjkim
293280304Sjkimint
29459191Skrissmb_ctx_setserver(struct smb_ctx *ctx, const char *name)
295280304Sjkim{
296280304Sjkim	if (strlen(name) > SMB_MAXSRVNAMELEN) {
297280304Sjkim		smb_error("server name '%s' too long", 0, name);
29859191Skris		return ENAMETOOLONG;
299280304Sjkim	}
300280304Sjkim	nls_str_upper(ctx->ct_ssn.ioc_srvname, name);
301280304Sjkim	return 0;
302280304Sjkim}
303280304Sjkim
304280304Sjkimint
305280304Sjkimsmb_ctx_setnbport(struct smb_ctx *ctx, int port)
306280304Sjkim{
307280304Sjkim	if (port < 1 || port > 0xffff)
308280304Sjkim		return EINVAL;
309280304Sjkim	ctx->ct_nb->nb_nmbtcpport = port;
310280304Sjkim	return 0;
311280304Sjkim}
312280304Sjkim
313280304Sjkimint
314280304Sjkimsmb_ctx_setsmbport(struct smb_ctx *ctx, int port)
315280304Sjkim{
316280304Sjkim	if (port < 1 || port > 0xffff)
317280304Sjkim		return EINVAL;
318280304Sjkim	ctx->ct_smbtcpport = port;
31959191Skris	ctx->ct_nb->nb_smbtcpport = port;
320280304Sjkim	return 0;
321280304Sjkim}
322280304Sjkim
323280304Sjkimint
324280304Sjkimsmb_ctx_setuser(struct smb_ctx *ctx, const char *name)
325280304Sjkim{
326280304Sjkim	if (strlen(name) > SMB_MAXUSERNAMELEN) {
327280304Sjkim		smb_error("user name '%s' too long", 0, name);
328280304Sjkim		return ENAMETOOLONG;
329280304Sjkim	}
330280304Sjkim	nls_str_upper(ctx->ct_ssn.ioc_user, name);
331162911Ssimon	return 0;
332280304Sjkim}
333280304Sjkim
334280304Sjkimint
335280304Sjkimsmb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name)
336280304Sjkim{
337280304Sjkim	if (strlen(name) > SMB_MAXUSERNAMELEN) {
33859191Skris		smb_error("workgroup name '%s' too long", 0, name);
339280304Sjkim		return ENAMETOOLONG;
340280304Sjkim	}
341280304Sjkim	nls_str_upper(ctx->ct_ssn.ioc_workgroup, name);
342280304Sjkim	return 0;
343280304Sjkim}
344280304Sjkim
345280304Sjkimint
346111147Snectarsmb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd)
347280304Sjkim{
348280304Sjkim	if (passwd == NULL)
34979998Skris		return EINVAL;
350280304Sjkim	if (strlen(passwd) > SMB_MAXPASSWORDLEN) {
351280304Sjkim		smb_error("password too long", 0);
352280304Sjkim		return ENAMETOOLONG;
353280304Sjkim	}
354280304Sjkim	if (strncmp(passwd, "$$1", 3) == 0)
355280304Sjkim		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
356280304Sjkim	else
357280304Sjkim		strcpy(ctx->ct_ssn.ioc_password, passwd);
358280304Sjkim	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
359280304Sjkim	return 0;
36059191Skris}
361280304Sjkim
362280304Sjkimint
363280304Sjkimsmb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
364280304Sjkim{
365280304Sjkim	if (strlen(share) > SMB_MAXSHARENAMELEN) {
36659191Skris		smb_error("share name '%s' too long", 0, share);
367280304Sjkim		return ENAMETOOLONG;
368280304Sjkim	}
369280304Sjkim	nls_str_upper(ctx->ct_sh.ioc_share, share);
370280304Sjkim	if (share[0] != 0)
371280304Sjkim		ctx->ct_parsedlevel = SMBL_SHARE;
372280304Sjkim	ctx->ct_sh.ioc_stype = stype;
373280304Sjkim	return 0;
374280304Sjkim}
375280304Sjkim
376280304Sjkimint
37759191Skrissmb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
378280304Sjkim{
379280304Sjkim	if (addr == NULL || addr[0] == 0)
380280304Sjkim		return EINVAL;
38159191Skris	if (ctx->ct_srvaddr)
382280304Sjkim		free(ctx->ct_srvaddr);
383280304Sjkim	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
384280304Sjkim		return ENOMEM;
385160814Ssimon	return 0;
386280304Sjkim}
387280304Sjkim
388280304Sjkimstatic int
389280304Sjkimsmb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
390280304Sjkim{
391280304Sjkim	struct group *gr;
39259191Skris	struct passwd *pw;
393280304Sjkim	char *cp;
394280304Sjkim
395280304Sjkim	cp = strchr(pair, ':');
396280304Sjkim	if (cp) {
397280304Sjkim		*cp++ = '\0';
398280304Sjkim		if (*cp) {
39959191Skris			gr = getgrnam(cp);
400280304Sjkim			if (gr) {
401280304Sjkim				*gid = gr->gr_gid;
402280304Sjkim			} else
403280304Sjkim				smb_error("Invalid group name %s, ignored",
404160814Ssimon				    0, cp);
405280304Sjkim		}
406280304Sjkim	}
407280304Sjkim	if (*pair) {
408280304Sjkim		pw = getpwnam(pair);
409280304Sjkim		if (pw) {
410280304Sjkim			*uid = pw->pw_uid;
411280304Sjkim		} else
412280304Sjkim			smb_error("Invalid user name %s, ignored", 0, pair);
413280304Sjkim	}
414280304Sjkim	endpwent();
415280304Sjkim	return 0;
416280304Sjkim}
417280304Sjkim
418280304Sjkimint
41959191Skrissmb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
42059191Skris{
42159191Skris	int error = 0;
422280304Sjkim	char *p, *cp;
423280304Sjkim
42459191Skris	switch(opt) {
42559191Skris	    case 'U':
42659191Skris		break;
42759191Skris	    case 'I':
428280304Sjkim		error = smb_ctx_setsrvaddr(ctx, arg);
429280304Sjkim		break;
430280304Sjkim	    case 'M':
43159191Skris		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
432		if (*cp == '/') {
433			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
434			ctx->ct_flags |= SMBCF_SRIGHTS;
435		}
436		break;
437	    case 'N':
438		ctx->ct_flags |= SMBCF_NOPWD;
439		break;
440	    case 'O':
441		p = strdup(arg);
442		cp = strchr(p, '/');
443		if (cp) {
444			*cp++ = '\0';
445			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
446			    &ctx->ct_sh.ioc_group);
447		}
448		if (*p && error == 0) {
449			error = smb_parse_owner(p, &ctx->ct_ssn.ioc_owner,
450			    &ctx->ct_ssn.ioc_group);
451		}
452		free(p);
453		break;
454	    case 'P':
455/*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT;*/
456		break;
457	    case 'R':
458		ctx->ct_ssn.ioc_retrycount = atoi(arg);
459		break;
460	    case 'T':
461		ctx->ct_ssn.ioc_timeout = atoi(arg);
462		break;
463	    case 'W':
464		error = smb_ctx_setworkgroup(ctx, arg);
465		break;
466	}
467	return error;
468}
469
470#if 0
471static void
472smb_hexdump(const u_char *buf, int len) {
473	int ofs = 0;
474
475	while (len--) {
476		if (ofs % 16 == 0)
477			printf("\n%02X: ", ofs);
478		printf("%02x ", *buf++);
479		ofs++;
480	}
481	printf("\n");
482}
483#endif
484
485
486static int
487smb_addiconvtbl(const char *to, const char *from, const u_char *tbl)
488{
489	int error;
490
491	error = kiconv_add_xlat_table(to, from, tbl);
492	if (error && error != EEXIST) {
493		smb_error("can not setup kernel iconv table (%s:%s)", error,
494		    from, to);
495		return error;
496	}
497	return 0;
498}
499
500/*
501 * Verify context before connect operation(s),
502 * lookup specified server and try to fill all forgotten fields.
503 */
504int
505smb_ctx_resolve(struct smb_ctx *ctx)
506{
507	struct smbioc_ossn *ssn = &ctx->ct_ssn;
508	struct smbioc_oshare *sh = &ctx->ct_sh;
509	struct nb_name nn;
510	struct sockaddr *sap;
511	struct sockaddr_nb *salocal, *saserver;
512	char *cp;
513	int error = 0;
514
515	ctx->ct_flags &= ~SMBCF_RESOLVED;
516	if (ssn->ioc_srvname[0] == 0) {
517		smb_error("no server name specified", 0);
518		return EINVAL;
519	}
520	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0) {
521		smb_error("no share name specified for %s@%s",
522		    0, ssn->ioc_user, ssn->ioc_srvname);
523		return EINVAL;
524	}
525	error = nb_ctx_resolve(ctx->ct_nb);
526	if (error)
527		return error;
528	if (ssn->ioc_localcs[0] == 0)
529		strcpy(ssn->ioc_localcs, "ISO8859-1");
530	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
531	if (error)
532		return error;
533	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
534	if (error)
535		return error;
536	if (ssn->ioc_servercs[0] != 0) {
537		error = kiconv_add_xlat16_cspairs
538			(ssn->ioc_servercs, ssn->ioc_localcs);
539		if (error) return error;
540	}
541	if (ctx->ct_srvaddr) {
542		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap, ctx->ct_smbtcpport);
543	} else {
544		error = nbns_resolvename(ssn->ioc_srvname, ctx->ct_nb, &sap);
545	}
546	if (error) {
547		smb_error("can't get server address", error);
548		return error;
549	}
550	nn.nn_scope = ctx->ct_nb->nb_scope;
551	nn.nn_type = NBT_SERVER;
552	if (strlen(ssn->ioc_srvname) > NB_NAMELEN)
553		return NBERROR(NBERR_NAMETOOLONG);
554	strlcpy(nn.nn_name, ssn->ioc_srvname, sizeof(nn.nn_name));
555	error = nb_sockaddr(sap, &nn, &saserver);
556	nb_snbfree(sap);
557	if (error) {
558		smb_error("can't allocate server address", error);
559		return error;
560	}
561	ssn->ioc_server = (struct sockaddr*)saserver;
562	if (ctx->ct_locname[0] == 0) {
563		error = nb_getlocalname(ctx->ct_locname);
564		if (error) {
565			smb_error("can't get local name", error);
566			return error;
567		}
568		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
569	}
570	/*
571	 * Truncate the local host name to NB_NAMELEN-1 which gives a
572	 * suffix of 0 which is "workstation name".
573	 */
574	strlcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN);
575	nn.nn_type = NBT_WKSTA;
576	nn.nn_scope = ctx->ct_nb->nb_scope;
577	error = nb_sockaddr(NULL, &nn, &salocal);
578	if (error) {
579		nb_snbfree((struct sockaddr*)saserver);
580		smb_error("can't allocate local address", error);
581		return error;
582	}
583	ssn->ioc_local = (struct sockaddr*)salocal;
584	ssn->ioc_lolen = salocal->snb_len;
585	ssn->ioc_svlen = saserver->snb_len;
586	if (ssn->ioc_password[0] == 0 && (ctx->ct_flags & SMBCF_NOPWD) == 0) {
587		cp = getpass("Password:");
588		error = smb_ctx_setpassword(ctx, cp);
589		if (error)
590			return error;
591	}
592	ctx->ct_flags |= SMBCF_RESOLVED;
593	return 0;
594}
595
596static int
597smb_ctx_gethandle(struct smb_ctx *ctx)
598{
599	int fd, i;
600	char buf[20];
601
602	fd = open("/dev/"NSMB_NAME, O_RDWR);
603	if (fd >= 0) {
604		ctx->ct_fd = fd;
605		return 0;
606	}
607	return ENOENT;
608}
609
610int
611smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
612{
613	struct smbioc_lookup rq;
614	int error;
615
616	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
617		smb_error("smb_ctx_lookup() data is not resolved", 0);
618		return EINVAL;
619	}
620	if (ctx->ct_fd != -1) {
621		close(ctx->ct_fd);
622		ctx->ct_fd = -1;
623	}
624	error = smb_ctx_gethandle(ctx);
625	if (error) {
626		smb_error("can't get handle to requester (no /dev/"NSMB_NAME"* device)", 0);
627		return EINVAL;
628	}
629	bzero(&rq, sizeof(rq));
630	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof(struct smbioc_ossn));
631	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof(struct smbioc_oshare));
632	rq.ioc_flags = flags;
633	rq.ioc_level = level;
634	if (ioctl(ctx->ct_fd, SMBIOC_LOOKUP, &rq) == -1) {
635		error = errno;
636		if (flags & SMBLK_CREATE)
637			smb_error("unable to open connection", error);
638		return error;
639	}
640	return 0;
641}
642
643int
644smb_ctx_login(struct smb_ctx *ctx)
645{
646	struct smbioc_ossn *ssn = &ctx->ct_ssn;
647	struct smbioc_oshare *sh = &ctx->ct_sh;
648	int error;
649
650	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
651		smb_error("smb_ctx_resolve() should be called first", 0);
652		return EINVAL;
653	}
654	if (ctx->ct_fd != -1) {
655		close(ctx->ct_fd);
656		ctx->ct_fd = -1;
657	}
658	error = smb_ctx_gethandle(ctx);
659	if (error) {
660		smb_error("can't get handle to requester", 0);
661		return EINVAL;
662	}
663	if (ioctl(ctx->ct_fd, SMBIOC_OPENSESSION, ssn) == -1) {
664		error = errno;
665		smb_error("can't open session to server %s", error, ssn->ioc_srvname);
666		return error;
667	}
668	if (sh->ioc_share[0] == 0)
669		return 0;
670	if (ioctl(ctx->ct_fd, SMBIOC_OPENSHARE, sh) == -1) {
671		error = errno;
672		smb_error("can't connect to share //%s/%s", error,
673		    ssn->ioc_srvname, sh->ioc_share);
674		return error;
675	}
676	return 0;
677}
678
679int
680smb_ctx_setflags(struct smb_ctx *ctx, int level, int mask, int flags)
681{
682	struct smbioc_flags fl;
683
684	if (ctx->ct_fd == -1)
685		return EINVAL;
686	fl.ioc_level = level;
687	fl.ioc_mask = mask;
688	fl.ioc_flags = flags;
689	if (ioctl(ctx->ct_fd, SMBIOC_SETFLAGS, &fl) == -1)
690		return errno;
691	return 0;
692}
693
694/*
695 * level values:
696 * 0 - default
697 * 1 - server
698 * 2 - server:user
699 * 3 - server:user:share
700 */
701static int
702smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
703{
704	char *p;
705	int error;
706
707	if (level >= 0) {
708		rc_getstringptr(smb_rc, sname, "charsets", &p);
709		if (p) {
710			error = smb_ctx_setcharset(ctx, p);
711			if (error)
712				smb_error("charset specification in the section '%s' ignored", error, sname);
713		}
714	}
715	if (level <= 1) {
716		rc_getint(smb_rc, sname, "timeout", &ctx->ct_ssn.ioc_timeout);
717		rc_getint(smb_rc, sname, "retry_count", &ctx->ct_ssn.ioc_retrycount);
718	}
719	if (level == 1) {
720		rc_getstringptr(smb_rc, sname, "addr", &p);
721		if (p) {
722			error = smb_ctx_setsrvaddr(ctx, p);
723			if (error) {
724				smb_error("invalid address specified in the section %s", 0, sname);
725				return error;
726			}
727		}
728	}
729	if (level >= 2) {
730		rc_getstringptr(smb_rc, sname, "password", &p);
731		if (p)
732			smb_ctx_setpassword(ctx, p);
733	}
734	rc_getstringptr(smb_rc, sname, "workgroup", &p);
735	if (p)
736		smb_ctx_setworkgroup(ctx, p);
737	return 0;
738}
739
740/*
741 * read rc file as follows:
742 * 1. read [default] section
743 * 2. override with [server] section
744 * 3. override with [server:user:share] section
745 * Since abcence of rcfile is not fatal, silently ignore this fact.
746 * smb_rc file should be closed by caller.
747 */
748int
749smb_ctx_readrc(struct smb_ctx *ctx)
750{
751	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + SMB_MAXSHARENAMELEN + 4];
752/*	char *p;*/
753
754	if (smb_open_rcfile() != 0)
755		return 0;
756
757	if (ctx->ct_ssn.ioc_user[0] == 0 || ctx->ct_ssn.ioc_srvname[0] == 0)
758		return 0;
759
760	smb_ctx_readrcsection(ctx, "default", 0);
761	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
762	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
763	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, ctx->ct_ssn.ioc_srvname, 1);
764	/*
765	 * SERVER:USER parameters
766	 */
767	snprintf(sname, sizeof(sname), "%s:%s", ctx->ct_ssn.ioc_srvname,
768	    ctx->ct_ssn.ioc_user);
769	smb_ctx_readrcsection(ctx, sname, 2);
770
771	if (ctx->ct_sh.ioc_share[0] != 0) {
772		/*
773		 * SERVER:USER:SHARE parameters
774	         */
775		snprintf(sname, sizeof(sname), "%s:%s:%s", ctx->ct_ssn.ioc_srvname,
776		    ctx->ct_ssn.ioc_user, ctx->ct_sh.ioc_share);
777		smb_ctx_readrcsection(ctx, sname, 3);
778	}
779	return 0;
780}
781
782