xref: /freebsd/contrib/smbfs/lib/smb/ctx.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
1 /*
2  * Copyright (c) 2000, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: ctx.c,v 1.22 2001/12/26 04:10:52 bp Exp $
33  */
34 #include <sys/param.h>
35 #include <sys/sysctl.h>
36 #include <sys/ioctl.h>
37 #include <sys/time.h>
38 #include <sys/mount.h>
39 #include <fcntl.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <pwd.h>
46 #include <grp.h>
47 #include <unistd.h>
48 #include <sys/iconv.h>
49 
50 #define NB_NEEDRESOLVER
51 
52 #include <netsmb/smb_lib.h>
53 #include <netsmb/netbios.h>
54 #include <netsmb/nb_lib.h>
55 #include <netsmb/smb_conn.h>
56 #include <cflib.h>
57 
58 /*
59  * Prescan command line for [-U user] argument
60  * and fill context with defaults
61  */
62 int
63 smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
64 	int minlevel, int maxlevel, int sharetype)
65 {
66 	int  opt, error = 0;
67 	const char *arg, *cp;
68 
69 	bzero(ctx,sizeof(*ctx));
70 	error = nb_ctx_create(&ctx->ct_nb);
71 	if (error)
72 		return error;
73 	ctx->ct_fd = -1;
74 	ctx->ct_parsedlevel = SMBL_NONE;
75 	ctx->ct_minlevel = minlevel;
76 	ctx->ct_maxlevel = maxlevel;
77 
78 	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE;
79 	ctx->ct_ssn.ioc_timeout = 15;
80 	ctx->ct_ssn.ioc_retrycount = 4;
81 	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
82 	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
83 	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
84 	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
85 
86 	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
87 	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
88 	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
89 	ctx->ct_sh.ioc_mode = SMBM_EXEC;
90 	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
91 	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
92 	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
93 
94 	nb_ctx_setscope(ctx->ct_nb, "");
95 	smb_ctx_setuser(ctx, getpwuid(geteuid())->pw_name);
96 	endpwent();
97 	if (argv == NULL)
98 		return 0;
99 	for (opt = 1; opt < argc; opt++) {
100 		cp = argv[opt];
101 		if (strncmp(cp, "//", 2) != 0)
102 			continue;
103 		error = smb_ctx_parseunc(ctx, cp, sharetype, (const char**)&cp);
104 		if (error)
105 			return error;
106 		ctx->ct_uncnext = cp;
107 		break;
108 	}
109 	while (error == 0 && (opt = cf_getopt(argc, argv, ":E:L:U:")) != -1) {
110 		arg = cf_optarg;
111 		switch (opt) {
112 		    case 'E':
113 			error = smb_ctx_setcharset(ctx, arg);
114 			if (error)
115 				return error;
116 			break;
117 		    case 'L':
118 			error = nls_setlocale(optarg);
119 			if (error)
120 				break;
121 			break;
122 		    case 'U':
123 			error = smb_ctx_setuser(ctx, arg);
124 			break;
125 		}
126 	}
127 	cf_optind = cf_optreset = 1;
128 	return error;
129 }
130 
131 void
132 smb_ctx_done(struct smb_ctx *ctx)
133 {
134 	if (ctx->ct_ssn.ioc_server)
135 		nb_snbfree(ctx->ct_ssn.ioc_server);
136 	if (ctx->ct_ssn.ioc_local)
137 		nb_snbfree(ctx->ct_ssn.ioc_local);
138 	if (ctx->ct_srvaddr)
139 		free(ctx->ct_srvaddr);
140 	if (ctx->ct_nb)
141 		nb_ctx_done(ctx->ct_nb);
142 }
143 
144 static int
145 getsubstring(const char *p, u_char sep, char *dest, int maxlen, const char **next)
146 {
147 	int len;
148 
149 	maxlen--;
150 	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
151 		if (*p == 0)
152 			return EINVAL;
153 		*dest = *p;
154 	}
155 	*dest = 0;
156 	*next = *p ? p + 1 : p;
157 	return 0;
158 }
159 
160 /*
161  * Here we expect something like "[proto:]//[user@]host[/share][/path]"
162  */
163 int
164 smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
165 	const char **next)
166 {
167 	const char *p = unc;
168 	char *p1;
169 	char tmp[1024];
170 	int error ;
171 
172 	ctx->ct_parsedlevel = SMBL_NONE;
173 	if (*p++ != '/' || *p++ != '/') {
174 		smb_error("UNC should start with '//'", 0);
175 		return EINVAL;
176 	}
177 	p1 = tmp;
178 	error = getsubstring(p, '@', p1, sizeof(tmp), &p);
179 	if (!error) {
180 		if (ctx->ct_maxlevel < SMBL_VC) {
181 			smb_error("no user name required", 0);
182 			return EINVAL;
183 		}
184 		if (*p1 == 0) {
185 			smb_error("empty user name", 0);
186 			return EINVAL;
187 		}
188 		error = smb_ctx_setuser(ctx, tmp);
189 		if (error)
190 			return error;
191 		ctx->ct_parsedlevel = SMBL_VC;
192 	}
193 	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
194 	if (error) {
195 		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
196 		if (error) {
197 			smb_error("no server name found", 0);
198 			return error;
199 		}
200 	}
201 	if (*p1 == 0) {
202 		smb_error("empty server name", 0);
203 		return EINVAL;
204 	}
205 	error = smb_ctx_setserver(ctx, tmp);
206 	if (error)
207 		return error;
208 	if (sharetype == SMB_ST_NONE) {
209 		*next = p;
210 		return 0;
211 	}
212 	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
213 		smb_error("no share name required", 0);
214 		return EINVAL;
215 	}
216 	error = getsubstring(p, '/', p1, sizeof(tmp), &p);
217 	if (error) {
218 		error = getsubstring(p, '\0', p1, sizeof(tmp), &p);
219 		if (error) {
220 			smb_error("unexpected end of line", 0);
221 			return error;
222 		}
223 	}
224 	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE) {
225 		smb_error("empty share name", 0);
226 		return EINVAL;
227 	}
228 	*next = p;
229 	if (*p1 == 0)
230 		return 0;
231 	error = smb_ctx_setshare(ctx, p1, sharetype);
232 	return error;
233 }
234 
235 int
236 smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
237 {
238 	char *cp, *servercs, *localcs;
239 	int cslen = sizeof(ctx->ct_ssn.ioc_localcs);
240 	int scslen, lcslen, error;
241 
242 	cp = strchr(arg, ':');
243 	lcslen = cp ? (cp - arg) : 0;
244 	if (lcslen == 0 || lcslen >= cslen) {
245 		smb_error("invalid local charset specification (%s)", 0, arg);
246 		return EINVAL;
247 	}
248 	scslen = (size_t)strlen(++cp);
249 	if (scslen == 0 || scslen >= cslen) {
250 		smb_error("invalid server charset specification (%s)", 0, arg);
251 		return EINVAL;
252 	}
253 	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
254 	localcs[lcslen] = 0;
255 	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
256 	error = nls_setrecode(localcs, servercs);
257 	if (error == 0)
258 		return 0;
259 	smb_error("can't initialize iconv support (%s:%s)",
260 	    error, localcs, servercs);
261 	localcs[0] = 0;
262 	servercs[0] = 0;
263 	return error;
264 }
265 
266 int
267 smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
268 {
269 	if (strlen(name) >= SMB_MAXSRVNAMELEN) {
270 		smb_error("server name '%s' too long", 0, name);
271 		return ENAMETOOLONG;
272 	}
273 	nls_str_upper(ctx->ct_ssn.ioc_srvname, name);
274 	return 0;
275 }
276 
277 int
278 smb_ctx_setuser(struct smb_ctx *ctx, const char *name)
279 {
280 	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
281 		smb_error("user name '%s' too long", 0, name);
282 		return ENAMETOOLONG;
283 	}
284 	nls_str_upper(ctx->ct_ssn.ioc_user, name);
285 	return 0;
286 }
287 
288 int
289 smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name)
290 {
291 	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
292 		smb_error("workgroup name '%s' too long", 0, name);
293 		return ENAMETOOLONG;
294 	}
295 	nls_str_upper(ctx->ct_ssn.ioc_workgroup, name);
296 	return 0;
297 }
298 
299 int
300 smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd)
301 {
302 	if (passwd == NULL)
303 		return EINVAL;
304 	if (strlen(passwd) >= SMB_MAXPASSWORDLEN) {
305 		smb_error("password too long", 0);
306 		return ENAMETOOLONG;
307 	}
308 	if (strncmp(passwd, "$$1", 3) == 0)
309 		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
310 	else
311 		strcpy(ctx->ct_ssn.ioc_password, passwd);
312 	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
313 	return 0;
314 }
315 
316 int
317 smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
318 {
319 	if (strlen(share) >= SMB_MAXSHARENAMELEN) {
320 		smb_error("share name '%s' too long", 0, share);
321 		return ENAMETOOLONG;
322 	}
323 	nls_str_upper(ctx->ct_sh.ioc_share, share);
324 	if (share[0] != 0)
325 		ctx->ct_parsedlevel = SMBL_SHARE;
326 	ctx->ct_sh.ioc_stype = stype;
327 	return 0;
328 }
329 
330 int
331 smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
332 {
333 	if (addr == NULL || addr[0] == 0)
334 		return EINVAL;
335 	if (ctx->ct_srvaddr)
336 		free(ctx->ct_srvaddr);
337 	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
338 		return ENOMEM;
339 	return 0;
340 }
341 
342 static int
343 smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
344 {
345 	struct group *gr;
346 	struct passwd *pw;
347 	char *cp;
348 
349 	cp = strchr(pair, ':');
350 	if (cp) {
351 		*cp++ = '\0';
352 		if (*cp) {
353 			gr = getgrnam(cp);
354 			if (gr) {
355 				*gid = gr->gr_gid;
356 			} else
357 				smb_error("Invalid group name %s, ignored",
358 				    0, cp);
359 		}
360 	}
361 	if (*pair) {
362 		pw = getpwnam(pair);
363 		if (pw) {
364 			*uid = pw->pw_uid;
365 		} else
366 			smb_error("Invalid user name %s, ignored", 0, pair);
367 	}
368 	endpwent();
369 	return 0;
370 }
371 
372 int
373 smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
374 {
375 	int error = 0;
376 	char *p, *cp;
377 
378 	switch(opt) {
379 	    case 'U':
380 		break;
381 	    case 'I':
382 		error = smb_ctx_setsrvaddr(ctx, arg);
383 		break;
384 	    case 'M':
385 		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
386 		if (*cp == '/') {
387 			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
388 			ctx->ct_flags |= SMBCF_SRIGHTS;
389 		}
390 		break;
391 	    case 'N':
392 		ctx->ct_flags |= SMBCF_NOPWD;
393 		break;
394 	    case 'O':
395 		p = strdup(arg);
396 		cp = strchr(p, '/');
397 		if (cp) {
398 			*cp++ = '\0';
399 			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
400 			    &ctx->ct_sh.ioc_group);
401 		}
402 		if (*p && error == 0) {
403 			error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner,
404 			    &ctx->ct_ssn.ioc_group);
405 		}
406 		free(p);
407 		break;
408 	    case 'P':
409 /*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT;*/
410 		break;
411 	    case 'R':
412 		ctx->ct_ssn.ioc_retrycount = atoi(arg);
413 		break;
414 	    case 'T':
415 		ctx->ct_ssn.ioc_timeout = atoi(arg);
416 		break;
417 	    case 'W':
418 		error = smb_ctx_setworkgroup(ctx, arg);
419 		break;
420 	}
421 	return error;
422 }
423 
424 #if 0
425 static void
426 smb_hexdump(const u_char *buf, int len) {
427 	int ofs = 0;
428 
429 	while (len--) {
430 		if (ofs % 16 == 0)
431 			printf("\n%02X: ", ofs);
432 		printf("%02x ", *buf++);
433 		ofs++;
434 	}
435 	printf("\n");
436 }
437 #endif
438 
439 
440 static int
441 smb_addiconvtbl(const char *to, const char *from, const u_char *tbl)
442 {
443 	int error;
444 
445 	error = kiconv_add_xlat_table(to, from, tbl);
446 	if (error && error != EEXIST) {
447 		smb_error("can not setup kernel iconv table (%s:%s)", error,
448 		    from, to);
449 		return error;
450 	}
451 	return 0;
452 }
453 
454 /*
455  * Verify context before connect operation(s),
456  * lookup specified server and try to fill all forgotten fields.
457  */
458 int
459 smb_ctx_resolve(struct smb_ctx *ctx)
460 {
461 	struct smbioc_ossn *ssn = &ctx->ct_ssn;
462 	struct smbioc_oshare *sh = &ctx->ct_sh;
463 	struct nb_name nn;
464 	struct sockaddr *sap;
465 	struct sockaddr_nb *salocal, *saserver;
466 	char *cp;
467 	u_char cstbl[256];
468 	u_int i;
469 	int error = 0;
470 
471 	ctx->ct_flags &= ~SMBCF_RESOLVED;
472 	if (ssn->ioc_srvname[0] == 0) {
473 		smb_error("no server name specified", 0);
474 		return EINVAL;
475 	}
476 	if (ssn->ioc_user[0] == 0) {
477 		smb_error("no user name specified for server %s",
478 		    0, ssn->ioc_srvname);
479 		return EINVAL;
480 	}
481 	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0) {
482 		smb_error("no share name specified for %s@%s",
483 		    0, ssn->ioc_user, ssn->ioc_srvname);
484 		return EINVAL;
485 	}
486 	error = nb_ctx_resolve(ctx->ct_nb);
487 	if (error)
488 		return error;
489 	if (ssn->ioc_localcs[0] == 0)
490 		strcpy(ssn->ioc_localcs, "default");	/* XXX: locale name ? */
491 	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
492 	if (error)
493 		return error;
494 	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
495 	if (error)
496 		return error;
497 	if (ssn->ioc_servercs[0] != 0) {
498 		for(i = 0; i < sizeof(cstbl); i++)
499 			cstbl[i] = i;
500 		nls_mem_toext(cstbl, cstbl, sizeof(cstbl));
501 		error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs, cstbl);
502 		if (error)
503 			return error;
504 		for(i = 0; i < sizeof(cstbl); i++)
505 			cstbl[i] = i;
506 		nls_mem_toloc(cstbl, cstbl, sizeof(cstbl));
507 		error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs, cstbl);
508 		if (error)
509 			return error;
510 	}
511 	if (ctx->ct_srvaddr) {
512 		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap);
513 	} else {
514 		error = nbns_resolvename(ssn->ioc_srvname, ctx->ct_nb, &sap);
515 	}
516 	if (error) {
517 		smb_error("can't get server address", error);
518 		return error;
519 	}
520 	nn.nn_scope = ctx->ct_nb->nb_scope;
521 	nn.nn_type = NBT_SERVER;
522 	strcpy(nn.nn_name, ssn->ioc_srvname);
523 	error = nb_sockaddr(sap, &nn, &saserver);
524 	nb_snbfree(sap);
525 	if (error) {
526 		smb_error("can't allocate server address", error);
527 		return error;
528 	}
529 	ssn->ioc_server = (struct sockaddr*)saserver;
530 	if (ctx->ct_locname[0] == 0) {
531 		error = nb_getlocalname(ctx->ct_locname);
532 		if (error) {
533 			smb_error("can't get local name", error);
534 			return error;
535 		}
536 		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
537 	}
538 	strcpy(nn.nn_name, ctx->ct_locname);
539 	nn.nn_type = NBT_WKSTA;
540 	nn.nn_scope = ctx->ct_nb->nb_scope;
541 	error = nb_sockaddr(NULL, &nn, &salocal);
542 	if (error) {
543 		nb_snbfree((struct sockaddr*)saserver);
544 		smb_error("can't allocate local address", error);
545 		return error;
546 	}
547 	ssn->ioc_local = (struct sockaddr*)salocal;
548 	ssn->ioc_lolen = salocal->snb_len;
549 	ssn->ioc_svlen = saserver->snb_len;
550 	if (ssn->ioc_password[0] == 0 && (ctx->ct_flags & SMBCF_NOPWD) == 0) {
551 		cp = getpass("Password:");
552 		error = smb_ctx_setpassword(ctx, cp);
553 		if (error)
554 			return error;
555 	}
556 	ctx->ct_flags |= SMBCF_RESOLVED;
557 	return 0;
558 }
559 
560 static int
561 smb_ctx_gethandle(struct smb_ctx *ctx)
562 {
563 	int fd, i;
564 	char buf[20];
565 
566 	/*
567 	 * First try to open as clone
568 	 */
569 	fd = open("/dev/"NSMB_NAME, O_RDWR);
570 	if (fd >= 0) {
571 		ctx->ct_fd = fd;
572 		return 0;
573 	}
574 	/*
575 	 * well, no clone capabilities available - we have to scan
576 	 * all devices in order to get free one
577 	 */
578 	 for (i = 0; i < 1024; i++) {
579 	         snprintf(buf, sizeof(buf), "/dev/%s%d", NSMB_NAME, i);
580 		 fd = open(buf, O_RDWR);
581 		 if (fd >= 0) {
582 			ctx->ct_fd = fd;
583 			return 0;
584 		 }
585 	 }
586 	 /*
587 	  * This is a compatibility with old /dev/net/nsmb device
588 	  */
589 	 for (i = 0; i < 1024; i++) {
590 	         snprintf(buf, sizeof(buf), "/dev/net/%s%d", NSMB_NAME, i);
591 		 fd = open(buf, O_RDWR);
592 		 if (fd >= 0) {
593 			ctx->ct_fd = fd;
594 			return 0;
595 		 }
596 		 if (errno == ENOENT)
597 		         return ENOENT;
598 	 }
599 	 return ENOENT;
600 }
601 
602 int
603 smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
604 {
605 	struct smbioc_lookup rq;
606 	int error;
607 
608 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
609 		smb_error("smb_ctx_lookup() data is not resolved", 0);
610 		return EINVAL;
611 	}
612 	if (ctx->ct_fd != -1) {
613 		close(ctx->ct_fd);
614 		ctx->ct_fd = -1;
615 	}
616 	error = smb_ctx_gethandle(ctx);
617 	if (error) {
618 		smb_error("can't get handle to requester (no /dev/"NSMB_NAME"* device)", 0);
619 		return EINVAL;
620 	}
621 	bzero(&rq, sizeof(rq));
622 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof(struct smbioc_ossn));
623 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof(struct smbioc_oshare));
624 	rq.ioc_flags = flags;
625 	rq.ioc_level = level;
626 	if (ioctl(ctx->ct_fd, SMBIOC_LOOKUP, &rq) == -1) {
627 		error = errno;
628 		if (flags & SMBLK_CREATE)
629 			smb_error("unable to open connection", error);
630 		return error;
631 	}
632 	return 0;
633 }
634 
635 int
636 smb_ctx_login(struct smb_ctx *ctx)
637 {
638 	struct smbioc_ossn *ssn = &ctx->ct_ssn;
639 	struct smbioc_oshare *sh = &ctx->ct_sh;
640 	int error;
641 
642 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
643 		smb_error("smb_ctx_resolve() should be called first", 0);
644 		return EINVAL;
645 	}
646 	if (ctx->ct_fd != -1) {
647 		close(ctx->ct_fd);
648 		ctx->ct_fd = -1;
649 	}
650 	error = smb_ctx_gethandle(ctx);
651 	if (error) {
652 		smb_error("can't get handle to requester", 0);
653 		return EINVAL;
654 	}
655 	if (ioctl(ctx->ct_fd, SMBIOC_OPENSESSION, ssn) == -1) {
656 		error = errno;
657 		smb_error("can't open session to server %s", error, ssn->ioc_srvname);
658 		return error;
659 	}
660 	if (sh->ioc_share[0] == 0)
661 		return 0;
662 	if (ioctl(ctx->ct_fd, SMBIOC_OPENSHARE, sh) == -1) {
663 		error = errno;
664 		smb_error("can't connect to share //%s/%s", error,
665 		    ssn->ioc_srvname, sh->ioc_share);
666 		return error;
667 	}
668 	return 0;
669 }
670 
671 int
672 smb_ctx_setflags(struct smb_ctx *ctx, int level, int mask, int flags)
673 {
674 	struct smbioc_flags fl;
675 
676 	if (ctx->ct_fd == -1)
677 		return EINVAL;
678 	fl.ioc_level = level;
679 	fl.ioc_mask = mask;
680 	fl.ioc_flags = flags;
681 	if (ioctl(ctx->ct_fd, SMBIOC_SETFLAGS, &fl) == -1)
682 		return errno;
683 	return 0;
684 }
685 
686 /*
687  * level values:
688  * 0 - default
689  * 1 - server
690  * 2 - server:user
691  * 3 - server:user:share
692  */
693 static int
694 smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
695 {
696 	char *p;
697 	int error;
698 
699 	if (level >= 0) {
700 		rc_getstringptr(smb_rc, sname, "charsets", &p);
701 		if (p) {
702 			error = smb_ctx_setcharset(ctx, p);
703 			if (error)
704 				smb_error("charset specification in the section '%s' ignored", error, sname);
705 		}
706 	}
707 	if (level <= 1) {
708 		rc_getint(smb_rc, sname, "timeout", &ctx->ct_ssn.ioc_timeout);
709 		rc_getint(smb_rc, sname, "retry_count", &ctx->ct_ssn.ioc_retrycount);
710 	}
711 	if (level == 1) {
712 		rc_getstringptr(smb_rc, sname, "addr", &p);
713 		if (p) {
714 			error = smb_ctx_setsrvaddr(ctx, p);
715 			if (error) {
716 				smb_error("invalid address specified in the section %s", 0, sname);
717 				return error;
718 			}
719 		}
720 	}
721 	if (level >= 2) {
722 		rc_getstringptr(smb_rc, sname, "password", &p);
723 		if (p)
724 			smb_ctx_setpassword(ctx, p);
725 	}
726 	rc_getstringptr(smb_rc, sname, "workgroup", &p);
727 	if (p)
728 		smb_ctx_setworkgroup(ctx, p);
729 	return 0;
730 }
731 
732 /*
733  * read rc file as follows:
734  * 1. read [default] section
735  * 2. override with [server] section
736  * 3. override with [server:user:share] section
737  * Since abcence of rcfile is not fatal, silently ignore this fact.
738  * smb_rc file should be closed by caller.
739  */
740 int
741 smb_ctx_readrc(struct smb_ctx *ctx)
742 {
743 	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN + SMB_MAXSHARENAMELEN + 4];
744 /*	char *p;*/
745 
746 	if (smb_open_rcfile() != 0)
747 		return 0;
748 
749 	if (ctx->ct_ssn.ioc_user[0] == 0 || ctx->ct_ssn.ioc_srvname[0] == 0)
750 		return 0;
751 
752 	smb_ctx_readrcsection(ctx, "default", 0);
753 	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
754 	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
755 	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, ctx->ct_ssn.ioc_srvname, 1);
756 	/*
757 	 * SERVER:USER parameters
758 	 */
759 	snprintf(sname, sizeof(sname), "%s:%s", ctx->ct_ssn.ioc_srvname,
760 	    ctx->ct_ssn.ioc_user);
761 	smb_ctx_readrcsection(ctx, sname, 2);
762 
763 	if (ctx->ct_sh.ioc_share[0] != 0) {
764 		/*
765 		 * SERVER:USER:SHARE parameters
766 	         */
767 		snprintf(sname, sizeof(sname), "%s:%s:%s", ctx->ct_ssn.ioc_srvname,
768 		    ctx->ct_ssn.ioc_user, ctx->ct_sh.ioc_share);
769 		smb_ctx_readrcsection(ctx, sname, 3);
770 	}
771 	return 0;
772 }
773 
774