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