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