xref: /titanic_44/usr/src/lib/libsmbfs/smb/ctx.c (revision 9853d9e82e7a067a2b88dae2fd257207e6be5f94)
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.32.70.2 2005/06/02 00:55:40 lindak Exp $
33  */
34 
35 /*
36  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
37  * Use is subject to license terms.
38  */
39 
40 #include <sys/param.h>
41 #include <sys/ioctl.h>
42 #include <sys/time.h>
43 #include <sys/mount.h>
44 #include <sys/types.h>
45 #include <sys/byteorder.h>
46 
47 #include <fcntl.h>
48 #include <ctype.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <stdlib.h>
54 #include <pwd.h>
55 #include <grp.h>
56 #include <unistd.h>
57 #include <libintl.h>
58 #include <assert.h>
59 #include <nss_dbdefs.h>
60 
61 #include <cflib.h>
62 #include <netsmb/smb_lib.h>
63 #include <netsmb/netbios.h>
64 #include <netsmb/nb_lib.h>
65 #include <netsmb/smb_dev.h>
66 
67 #include "charsets.h"
68 #include "spnego.h"
69 #include "derparse.h"
70 #include "private.h"
71 #include "ntlm.h"
72 
73 #ifndef FALSE
74 #define	FALSE	0
75 #endif
76 #ifndef TRUE
77 #define	TRUE	1
78 #endif
79 
80 
81 /* These two may be set by commands. */
82 int smb_debug, smb_verbose;
83 
84 /*
85  * Was: STDPARAM_OPT - see smb_ctx_scan_argv, smb_ctx_opt
86  */
87 const char smbutil_std_opts[] = "ABCD:E:I:L:M:NO:P:U:R:S:T:W:";
88 
89 /*
90  * Give the RPC library a callback hook that will be
91  * called whenever we destroy or reinit an smb_ctx_t.
92  * The name rpc_cleanup_smbctx() is legacy, and was
93  * originally a direct call into the RPC code.
94  */
95 static smb_ctx_close_hook_t close_hook;
96 static void
97 rpc_cleanup_smbctx(struct smb_ctx *ctx)
98 {
99 	if (close_hook)
100 		(*close_hook)(ctx);
101 }
102 void
103 smb_ctx_set_close_hook(smb_ctx_close_hook_t hook)
104 {
105 	close_hook = hook;
106 }
107 
108 void
109 dump_ctx_flags(int flags)
110 {
111 	printf(" Flags: ");
112 	if (flags == 0)
113 		printf("0");
114 	if (flags & SMBCF_NOPWD)
115 		printf("NOPWD ");
116 	if (flags & SMBCF_SRIGHTS)
117 		printf("SRIGHTS ");
118 	if (flags & SMBCF_LOCALE)
119 		printf("LOCALE ");
120 	if (flags & SMBCF_CMD_DOM)
121 		printf("CMD_DOM ");
122 	if (flags & SMBCF_CMD_USR)
123 		printf("CMD_USR ");
124 	if (flags & SMBCF_CMD_PW)
125 		printf("CMD_PW ");
126 	if (flags & SMBCF_RESOLVED)
127 		printf("RESOLVED ");
128 	if (flags & SMBCF_KCBAD)
129 		printf("KCBAD ");
130 	if (flags & SMBCF_KCFOUND)
131 		printf("KCFOUND ");
132 	if (flags & SMBCF_BROWSEOK)
133 		printf("BROWSEOK ");
134 	if (flags & SMBCF_AUTHREQ)
135 		printf("AUTHREQ ");
136 	if (flags & SMBCF_KCSAVE)
137 		printf("KCSAVE  ");
138 	if (flags & SMBCF_XXX)
139 		printf("XXX ");
140 	if (flags & SMBCF_SSNACTIVE)
141 		printf("SSNACTIVE ");
142 	if (flags & SMBCF_KCDOMAIN)
143 		printf("KCDOMAIN ");
144 	printf("\n");
145 }
146 
147 void
148 dump_iod_ssn(smb_iod_ssn_t *is)
149 {
150 	static const char zeros[NTLM_HASH_SZ] = {0};
151 	struct smbioc_ossn *ssn = &is->iod_ossn;
152 
153 	printf(" ct_srvname=\"%s\", ", ssn->ssn_srvname);
154 	dump_sockaddr(&ssn->ssn_srvaddr.sa);
155 	printf(" dom=\"%s\", user=\"%s\"\n",
156 	    ssn->ssn_domain, ssn->ssn_user);
157 	printf(" ct_vopt=0x%x, ct_owner=%d\n",
158 	    ssn->ssn_vopt, ssn->ssn_owner);
159 	printf(" ct_authflags=0x%x\n", is->iod_authflags);
160 
161 	printf(" ct_nthash:");
162 	if (bcmp(zeros, &is->iod_nthash, NTLM_HASH_SZ))
163 		smb_hexdump(&is->iod_nthash, NTLM_HASH_SZ);
164 	else
165 		printf(" {0}\n");
166 
167 	printf(" ct_lmhash:");
168 	if (bcmp(zeros, &is->iod_lmhash, NTLM_HASH_SZ))
169 		smb_hexdump(&is->iod_lmhash, NTLM_HASH_SZ);
170 	else
171 		printf(" {0}\n");
172 }
173 
174 void
175 dump_ctx(char *where, struct smb_ctx *ctx)
176 {
177 	printf("context %s:\n", where);
178 	dump_ctx_flags(ctx->ct_flags);
179 
180 	if (ctx->ct_locname)
181 		printf(" localname=\"%s\"", ctx->ct_locname);
182 	else
183 		printf(" localname=NULL");
184 
185 	if (ctx->ct_fullserver)
186 		printf(" fullserver=\"%s\"", ctx->ct_fullserver);
187 	else
188 		printf(" fullserver=NULL");
189 
190 	if (ctx->ct_srvaddr_s)
191 		printf(" srvaddr_s=\"%s\"\n", ctx->ct_srvaddr_s);
192 	else
193 		printf(" srvaddr_s=NULL\n");
194 
195 	if (ctx->ct_addrinfo)
196 		dump_addrinfo(ctx->ct_addrinfo);
197 	else
198 		printf(" ct_addrinfo = NULL\n");
199 
200 	dump_iod_ssn(&ctx->ct_iod_ssn);
201 
202 	printf(" share_name=\"%s\", share_type=%d\n",
203 	    ctx->ct_origshare ? ctx->ct_origshare : "",
204 	    ctx->ct_shtype_req);
205 
206 	/* dump_iod_work()? */
207 }
208 
209 int
210 smb_ctx_alloc(struct smb_ctx **ctx_pp)
211 {
212 	smb_ctx_t *ctx;
213 	int err;
214 
215 	ctx = malloc(sizeof (*ctx));
216 	if (ctx == NULL)
217 		return (ENOMEM);
218 	err = smb_ctx_init(ctx);
219 	if (err != 0) {
220 		free(ctx);
221 		return (err);
222 	}
223 	*ctx_pp = ctx;
224 	return (0);
225 }
226 
227 /*
228  * Initialize an smb_ctx struct (defaults)
229  */
230 int
231 smb_ctx_init(struct smb_ctx *ctx)
232 {
233 	char pwbuf[NSS_BUFLEN_PASSWD];
234 	struct passwd pw;
235 	int error = 0;
236 
237 	bzero(ctx, sizeof (*ctx));
238 
239 	error = nb_ctx_create(&ctx->ct_nb);
240 	if (error)
241 		return (error);
242 
243 	ctx->ct_dev_fd = -1;
244 	ctx->ct_tran_fd = -1;
245 	ctx->ct_parsedlevel = SMBL_NONE;
246 	ctx->ct_minlevel = SMBL_NONE;
247 	ctx->ct_maxlevel = SMBL_PATH;
248 
249 	/* Fill in defaults */
250 	ctx->ct_vopt = SMBVOPT_EXT_SEC;
251 	ctx->ct_owner = SMBM_ANY_OWNER;
252 	ctx->ct_authflags = SMB_AT_DEFAULT;
253 	ctx->ct_minauth = SMB_AT_DEFAULT;
254 
255 	nb_ctx_setscope(ctx->ct_nb, "");
256 
257 	/*
258 	 * if the user name is not specified some other way,
259 	 * use the current user name (built-in default)
260 	 */
261 	if (getpwuid_r(getuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL) {
262 		smb_ctx_setuser(ctx, pw.pw_name, 0);
263 		ctx->ct_home = strdup(pw.pw_name);
264 	}
265 
266 	/*
267 	 * Set a built-in default domain (workgroup).
268 	 * Using the Windows/NT default for now.
269 	 */
270 	smb_ctx_setdomain(ctx, "WORKGROUP", 0);
271 
272 	return (error);
273 }
274 
275 /*
276  * "Scan" the command line args to find the server name,
277  * user name, and share name, as needed.  We need these
278  * before reading the RC files and/or sharectl values.
279  *
280  * The sequence for getting all the members filled in
281  * has some tricky aspects.  Here's how it works:
282  *
283  * The search order for options is as follows:
284  *   command line options
285  *   values parsed from UNC path (cmd)
286  *   values from RC file (per-user)
287  *   values from SMF (system-wide)
288  *   built-in defaults
289  *
290  * Normally, one would simply get all the values starting with
291  * the bottom of the above list and working to the top, and
292  * overwriting values as you go.  But we need an exception.
293  *
294  * In this function, we parse the UNC path and command line options,
295  * because we need (at least) the server name when we're getting the
296  * SMF and RC file values.  However, values we get from the command
297  * should not be overwritten by SMF or RC file parsing, so we mark
298  * values from the command as "from CMD" and the RC file parser
299  * leaves in place any values so marked.  See: SMBCF_CMD_*
300  *
301  * The semantics of these flags are: "This value came from the
302  * current command instance, not from sources that may apply to
303  * multiple commands."  (Different from the old "FROMUSR" flag.)
304  *
305  * Note that smb_ctx_opt() is called later to handle the
306  * remaining options, which should be ignored here.
307  * The (magic) leading ":" in cf_getopt() makes it
308  * ignore options not in the options string.
309  */
310 int
311 smb_ctx_scan_argv(struct smb_ctx *ctx, int argc, char **argv,
312 	int minlevel, int maxlevel, int sharetype)
313 {
314 	int  ind, opt, error = 0;
315 	int aflg = 0, uflg = 0;
316 	const char *arg;
317 
318 	/*
319 	 * Parse options, if any.  Values from here too
320 	 * are marked as "from CMD".
321 	 */
322 	if (argv == NULL)
323 		return (0);
324 
325 	ctx->ct_minlevel = minlevel;
326 	ctx->ct_maxlevel = maxlevel;
327 	ctx->ct_shtype_req = sharetype;
328 
329 	cf_opt_lock();
330 	/* Careful: no return/goto before cf_opt_unlock! */
331 	while (error == 0) {
332 		opt = cf_getopt(argc, argv, STDPARAM_OPT);
333 		if (opt == -1)
334 			break;
335 		arg = cf_optarg;
336 		/* NB: handle most in smb_ctx_opt */
337 		switch (opt) {
338 		case 'A':
339 			aflg = 1;
340 			error = smb_ctx_setuser(ctx, "", TRUE);
341 			ctx->ct_flags |= SMBCF_NOPWD;
342 			break;
343 		case 'U':
344 			uflg = 1;
345 			error = smb_ctx_setuser(ctx, arg, TRUE);
346 			break;
347 		default:
348 			DPRINT("skip opt=%c", opt);
349 			break;
350 		}
351 	}
352 	ind = cf_optind;
353 	arg = argv[ind];
354 	cf_optind = cf_optreset = 1;
355 	cf_opt_unlock();
356 
357 	if (error)
358 		return (error);
359 
360 	if (aflg && uflg)  {
361 		printf(gettext("-A and -U flags are exclusive.\n"));
362 		return (EINVAL);
363 	}
364 
365 	/*
366 	 * Parse the UNC path.  Values from here are
367 	 * marked as "from CMD".
368 	 */
369 	for (; ind < argc; ind++) {
370 		arg = argv[ind];
371 		if (strncmp(arg, "//", 2) != 0)
372 			continue;
373 		error = smb_ctx_parseunc(ctx, arg,
374 		    minlevel, maxlevel, sharetype, &arg);
375 		if (error)
376 			return (error);
377 		break;
378 	}
379 
380 	return (error);
381 }
382 
383 void
384 smb_ctx_free(smb_ctx_t *ctx)
385 {
386 	smb_ctx_done(ctx);
387 	free(ctx);
388 }
389 
390 void
391 smb_ctx_done(struct smb_ctx *ctx)
392 {
393 
394 	rpc_cleanup_smbctx(ctx);
395 
396 	if (ctx->ct_dev_fd != -1) {
397 		close(ctx->ct_dev_fd);
398 		ctx->ct_dev_fd = -1;
399 	}
400 	if (ctx->ct_tran_fd != -1) {
401 		close(ctx->ct_tran_fd);
402 		ctx->ct_tran_fd = -1;
403 	}
404 	if (ctx->ct_srvaddr_s) {
405 		free(ctx->ct_srvaddr_s);
406 		ctx->ct_srvaddr_s = NULL;
407 	}
408 	if (ctx->ct_nb) {
409 		nb_ctx_done(ctx->ct_nb);
410 		ctx->ct_nb = NULL;
411 	}
412 	if (ctx->ct_locname) {
413 		free(ctx->ct_locname);
414 		ctx->ct_locname = NULL;
415 	}
416 	if (ctx->ct_origshare) {
417 		free(ctx->ct_origshare);
418 		ctx->ct_origshare = NULL;
419 	}
420 	if (ctx->ct_fullserver) {
421 		free(ctx->ct_fullserver);
422 		ctx->ct_fullserver = NULL;
423 	}
424 	if (ctx->ct_addrinfo) {
425 		freeaddrinfo(ctx->ct_addrinfo);
426 		ctx->ct_addrinfo = NULL;
427 	}
428 	if (ctx->ct_home)
429 		free(ctx->ct_home);
430 	if (ctx->ct_srv_OS) {
431 		free(ctx->ct_srv_OS);
432 		ctx->ct_srv_OS = NULL;
433 	}
434 	if (ctx->ct_srv_LM) {
435 		free(ctx->ct_srv_LM);
436 		ctx->ct_srv_LM = NULL;
437 	}
438 	if (ctx->ct_mackey) {
439 		free(ctx->ct_mackey);
440 		ctx->ct_mackey = NULL;
441 	}
442 }
443 
444 static int
445 getsubstring(const char *p, uchar_t sep, char *dest, int maxlen,
446     const char **next)
447 {
448 	int len;
449 
450 	maxlen--;
451 	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
452 		if (*p == 0)
453 			return (EINVAL);
454 		*dest = *p;
455 	}
456 	*dest = 0;
457 	*next = *p ? p + 1 : p;
458 	return (0);
459 }
460 
461 /*
462  * Parse the UNC path.  Here we expect something like
463  *   "//[workgroup;][user[:password]@]host[/share[/path]]"
464  * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
465  * Values found here are marked as "from CMD".
466  */
467 int
468 smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc,
469 	int minlevel, int maxlevel, int sharetype,
470 	const char **next)
471 {
472 	const char *p = unc;
473 	char *p1, *colon;
474 	char tmp[1024];
475 	char tmp2[1024];
476 	int error;
477 
478 	/*
479 	 * This may be called outside of _scan_argv,
480 	 * so make sure these get initialized.
481 	 */
482 	ctx->ct_minlevel = minlevel;
483 	ctx->ct_maxlevel = maxlevel;
484 	ctx->ct_shtype_req = sharetype;
485 
486 	ctx->ct_parsedlevel = SMBL_NONE;
487 	if (*p++ != '/' || *p++ != '/') {
488 		smb_error(dgettext(TEXT_DOMAIN,
489 		    "UNC should start with '//'"), 0);
490 		error = EINVAL;
491 		goto out;
492 	}
493 	p1 = tmp;
494 	error = getsubstring(p, ';', p1, sizeof (tmp), &p);
495 	if (!error) {
496 		if (*p1 == 0) {
497 			smb_error(dgettext(TEXT_DOMAIN,
498 			    "empty workgroup name"), 0);
499 			error = EINVAL;
500 			goto out;
501 		}
502 		nls_str_upper(tmp, tmp);
503 		error = smb_ctx_setdomain(ctx, unpercent(tmp), TRUE);
504 		if (error)
505 			goto out;
506 	}
507 	colon = (char *)p;
508 	error = getsubstring(p, '@', p1, sizeof (tmp), &p);
509 	if (!error) {
510 		if (ctx->ct_maxlevel < SMBL_VC) {
511 			smb_error(dgettext(TEXT_DOMAIN,
512 			    "no user name required"), 0);
513 			error = EINVAL;
514 			goto out;
515 		}
516 		p1 = strchr(tmp, ':');
517 		if (p1) {
518 			colon += p1 - tmp;
519 			*p1++ = (char)0;
520 			error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE);
521 			if (error)
522 				goto out;
523 			if (p - colon > 2)
524 				memset(colon+1, '*', p - colon - 2);
525 		}
526 		p1 = tmp;
527 		if (*p1 == 0) {
528 			smb_error(dgettext(TEXT_DOMAIN,
529 			    "empty user name"), 0);
530 			error = EINVAL;
531 			goto out;
532 		}
533 		error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE);
534 		if (error)
535 			goto out;
536 		ctx->ct_parsedlevel = SMBL_VC;
537 	}
538 	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
539 	if (error) {
540 		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
541 		if (error) {
542 			smb_error(dgettext(TEXT_DOMAIN,
543 			    "no server name found"), 0);
544 			goto out;
545 		}
546 	}
547 	if (*p1 == 0) {
548 		smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
549 		error = EINVAL;
550 		goto out;
551 	}
552 
553 	/*
554 	 * It's safe to uppercase this string, which
555 	 * consists of ascii characters that should
556 	 * be uppercased, %s, and ascii characters representing
557 	 * hex digits 0-9 and A-F (already uppercased, and
558 	 * if not uppercased they need to be). However,
559 	 * it is NOT safe to uppercase after it has been
560 	 * "unpercent" converted, below!
561 	 */
562 	nls_str_upper(tmp2, tmp);
563 
564 	/*
565 	 * Save ct_fullserver without case conversion.
566 	 */
567 	if (strchr(tmp, '%'))
568 		(void) unpercent(tmp);
569 	smb_ctx_setfullserver(ctx, tmp);
570 	if (error)
571 		goto out;
572 
573 #ifdef	SMB_ST_NONE
574 	if (sharetype == SMB_ST_NONE) {
575 		if (next)
576 			*next = p;
577 		error = 0;
578 		goto out;
579 	}
580 #endif
581 
582 	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
583 		smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0);
584 		error = EINVAL;
585 		goto out;
586 	}
587 	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
588 	if (error) {
589 		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
590 		if (error) {
591 			smb_error(dgettext(TEXT_DOMAIN,
592 			    "unexpected end of line"), 0);
593 			goto out;
594 		}
595 	}
596 	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE &&
597 	    !(ctx->ct_flags & SMBCF_BROWSEOK)) {
598 		smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
599 		error = EINVAL;
600 		goto out;
601 	}
602 	if (next)
603 		*next = p;
604 	if (*p1 == 0) {
605 		error = 0;
606 		goto out;
607 	}
608 	error = smb_ctx_setshare(ctx, unpercent(p1), sharetype);
609 
610 out:
611 	if (error == 0 && smb_debug > 0)
612 		dump_ctx("after smb_ctx_parseunc", ctx);
613 
614 	return (error);
615 }
616 
617 #ifdef KICONV_SUPPORT
618 int
619 smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
620 {
621 	char *cp, *servercs, *localcs;
622 	int cslen = sizeof (ctx->ct_ssn.ioc_localcs);
623 	int scslen, lcslen, error;
624 
625 	cp = strchr(arg, ':');
626 	lcslen = cp ? (cp - arg) : 0;
627 	if (lcslen == 0 || lcslen >= cslen) {
628 		smb_error(dgettext(TEXT_DOMAIN,
629 		    "invalid local charset specification (%s)"), 0, arg);
630 		return (EINVAL);
631 	}
632 	scslen = (size_t)strlen(++cp);
633 	if (scslen == 0 || scslen >= cslen) {
634 		smb_error(dgettext(TEXT_DOMAIN,
635 		    "invalid server charset specification (%s)"), 0, arg);
636 		return (EINVAL);
637 	}
638 	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
639 	localcs[lcslen] = 0;
640 	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
641 	error = nls_setrecode(localcs, servercs);
642 	if (error == 0)
643 		return (0);
644 	smb_error(dgettext(TEXT_DOMAIN,
645 	    "can't initialize iconv support (%s:%s)"),
646 	    error, localcs, servercs);
647 	localcs[0] = 0;
648 	servercs[0] = 0;
649 	return (error);
650 }
651 #endif /* KICONV_SUPPORT */
652 
653 int
654 smb_ctx_setauthflags(struct smb_ctx *ctx, int flags)
655 {
656 	ctx->ct_authflags = flags;
657 	return (0);
658 }
659 
660 int
661 smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
662 {
663 	char *p = strdup(name);
664 
665 	if (p == NULL)
666 		return (ENOMEM);
667 	if (ctx->ct_fullserver)
668 		free(ctx->ct_fullserver);
669 	ctx->ct_fullserver = p;
670 	return (0);
671 }
672 
673 /* this routine does not uppercase the server name */
674 int
675 smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
676 {
677 	/* don't uppercase the server name */
678 	strlcpy(ctx->ct_srvname, name,
679 	    sizeof (ctx->ct_srvname));
680 	return (0);
681 }
682 
683 int
684 smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
685 {
686 
687 	if (strlen(name) >= sizeof (ctx->ct_user)) {
688 		smb_error(dgettext(TEXT_DOMAIN,
689 		    "user name '%s' too long"), 0, name);
690 		return (ENAMETOOLONG);
691 	}
692 
693 	/*
694 	 * Don't overwrite a value from the command line
695 	 * with one from anywhere else.
696 	 */
697 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR))
698 		return (0);
699 
700 	/* don't uppercase the username, just copy it. */
701 	strlcpy(ctx->ct_user, name,
702 	    sizeof (ctx->ct_user));
703 
704 	/* Mark this as "from the command line". */
705 	if (from_cmd)
706 		ctx->ct_flags |= SMBCF_CMD_USR;
707 
708 	return (0);
709 }
710 
711 /*
712  * Never uppercase the workgroup
713  * name here, because it might come
714  * from a Windows codepage encoding.
715  *
716  * Don't overwrite a domain name from the
717  * command line with one from anywhere else.
718  * See smb_ctx_init() for notes about this.
719  */
720 int
721 smb_ctx_setdomain(struct smb_ctx *ctx, const char *name, int from_cmd)
722 {
723 
724 	if (strlen(name) >= sizeof (ctx->ct_domain)) {
725 		smb_error(dgettext(TEXT_DOMAIN,
726 		    "workgroup name '%s' too long"), 0, name);
727 		return (ENAMETOOLONG);
728 	}
729 
730 	/*
731 	 * Don't overwrite a value from the command line
732 	 * with one from anywhere else.
733 	 */
734 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
735 		return (0);
736 
737 	strlcpy(ctx->ct_domain, name,
738 	    sizeof (ctx->ct_domain));
739 
740 	/* Mark this as "from the command line". */
741 	if (from_cmd)
742 		ctx->ct_flags |= SMBCF_CMD_DOM;
743 
744 	return (0);
745 }
746 
747 int
748 smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
749 {
750 	int err;
751 
752 	if (passwd == NULL)
753 		return (EINVAL);
754 	if (strlen(passwd) >= sizeof (ctx->ct_password)) {
755 		smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
756 		return (ENAMETOOLONG);
757 	}
758 
759 	/*
760 	 * If called again after comand line parsing,
761 	 * don't overwrite a value from the command line
762 	 * with one from any stored config.
763 	 */
764 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW))
765 		return (0);
766 
767 	memset(ctx->ct_password, 0, sizeof (ctx->ct_password));
768 	if (strncmp(passwd, "$$1", 3) == 0)
769 		smb_simpledecrypt(ctx->ct_password, passwd);
770 	else
771 		strlcpy(ctx->ct_password, passwd,
772 		    sizeof (ctx->ct_password));
773 
774 	/*
775 	 * Compute LM hash, NT hash.
776 	 */
777 	if (ctx->ct_password[0]) {
778 		err = ntlm_compute_nt_hash(ctx->ct_nthash, ctx->ct_password);
779 		if (err != 0)
780 			return (err);
781 		err = ntlm_compute_lm_hash(ctx->ct_lmhash, ctx->ct_password);
782 		if (err != 0)
783 			return (err);
784 	}
785 
786 	/* Mark this as "from the command line". */
787 	if (from_cmd)
788 		ctx->ct_flags |= SMBCF_CMD_PW;
789 
790 	return (0);
791 }
792 
793 /*
794  * Use this to set NTLM auth. info (hashes)
795  * when we don't have the password.
796  */
797 int
798 smb_ctx_setpwhash(smb_ctx_t *ctx,
799     const uchar_t *nthash, const uchar_t *lmhash)
800 {
801 
802 	/* Need ct_password to be non-null. */
803 	if (ctx->ct_password[0] == '\0')
804 		strlcpy(ctx->ct_password, "$HASH",
805 		    sizeof (ctx->ct_password));
806 
807 	/*
808 	 * Compute LM hash, NT hash.
809 	 */
810 	memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
811 
812 	/* The LM hash is optional */
813 	if (lmhash) {
814 		memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
815 	}
816 
817 	return (0);
818 }
819 
820 int
821 smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
822 {
823 	if (strlen(share) >= SMBIOC_MAX_NAME) {
824 		smb_error(dgettext(TEXT_DOMAIN,
825 		    "share name '%s' too long"), 0, share);
826 		return (ENAMETOOLONG);
827 	}
828 	if (ctx->ct_origshare)
829 		free(ctx->ct_origshare);
830 	if ((ctx->ct_origshare = strdup(share)) == NULL)
831 		return (ENOMEM);
832 
833 	ctx->ct_shtype_req = stype;
834 
835 	return (0);
836 }
837 
838 int
839 smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
840 {
841 	if (addr == NULL || addr[0] == 0)
842 		return (EINVAL);
843 	if (ctx->ct_srvaddr_s)
844 		free(ctx->ct_srvaddr_s);
845 	if ((ctx->ct_srvaddr_s = strdup(addr)) == NULL)
846 		return (ENOMEM);
847 	return (0);
848 }
849 
850 static int
851 smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
852 {
853 	struct group gr;
854 	struct passwd pw;
855 	char buf[NSS_BUFLEN_PASSWD];
856 	char *cp;
857 
858 	cp = strchr(pair, ':');
859 	if (cp) {
860 		*cp++ = '\0';
861 		if (*cp && gid) {
862 			if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) {
863 				*gid = gr.gr_gid;
864 			} else
865 				smb_error(dgettext(TEXT_DOMAIN,
866 				    "Invalid group name %s, ignored"), 0, cp);
867 		}
868 	}
869 	if (*pair) {
870 		if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) {
871 			*uid = pw.pw_uid;
872 		} else
873 			smb_error(dgettext(TEXT_DOMAIN,
874 			    "Invalid user name %s, ignored"), 0, pair);
875 	}
876 
877 	return (0);
878 }
879 
880 /*
881  * Commands use this with getopt.  See:
882  *   STDPARAM_OPT, STDPARAM_ARGS
883  * Called after smb_ctx_readrc().
884  */
885 int
886 smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
887 {
888 	int error = 0;
889 	char *p, *cp;
890 	char tmp[1024];
891 
892 	switch (opt) {
893 	case 'A':
894 	case 'U':
895 		/* Handled in smb_ctx_init() */
896 		break;
897 	case 'I':
898 		error = smb_ctx_setsrvaddr(ctx, arg);
899 		break;
900 	case 'M':
901 		/* share connect rights - ignored */
902 		ctx->ct_flags |= SMBCF_SRIGHTS;
903 		break;
904 	case 'N':
905 		ctx->ct_flags |= SMBCF_NOPWD;
906 		break;
907 	case 'O':
908 		p = strdup(arg);
909 		cp = strchr(p, '/');
910 		if (cp)
911 			*cp = '\0';
912 		error = smb_parse_owner(cp, &ctx->ct_owner, NULL);
913 		free(p);
914 		break;
915 	case 'P':
916 /*		ctx->ct_vopt |= SMBCOPT_PERMANENT; */
917 		break;
918 	case 'R':
919 		/* retry count - ignored */
920 		break;
921 	case 'T':
922 		/* timeout - ignored */
923 		break;
924 	case 'D':	/* domain */
925 	case 'W':	/* workgroup (legacy alias) */
926 		nls_str_upper(tmp, arg);
927 		error = smb_ctx_setdomain(ctx, tmp, TRUE);
928 		break;
929 	}
930 	return (error);
931 }
932 
933 
934 /*
935  * Original code injected iconv tables into the kernel.
936  * Not sure if we'll need this or not...  REVISIT
937  */
938 #ifdef KICONV_SUPPORT
939 static int
940 smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl)
941 {
942 	int error = 0;
943 
944 	error = kiconv_add_xlat_table(to, from, tbl);
945 	if (error && error != EEXIST) {
946 		smb_error(dgettext(TEXT_DOMAIN,
947 		    "can not setup kernel iconv table (%s:%s)"),
948 		    error, from, to);
949 		return (error);
950 	}
951 	return (error);
952 }
953 #endif	/* KICONV_SUPPORT */
954 
955 /*
956  * Verify context info. before connect operation(s),
957  * lookup specified server and try to fill all forgotten fields.
958  * Legacy name used by commands.
959  */
960 int
961 smb_ctx_resolve(struct smb_ctx *ctx)
962 {
963 	struct smbioc_ossn *ssn = &ctx->ct_ssn;
964 	int error = 0;
965 #ifdef KICONV_SUPPORT
966 	uchar_t cstbl[256];
967 	uint_t i;
968 #endif
969 
970 	ctx->ct_flags &= ~SMBCF_RESOLVED;
971 
972 	if (ctx->ct_fullserver == NULL) {
973 		smb_error(dgettext(TEXT_DOMAIN,
974 		    "no server name specified"), 0);
975 		return (EINVAL);
976 	}
977 
978 	if (ctx->ct_minlevel >= SMBL_SHARE &&
979 	    ctx->ct_origshare == NULL) {
980 		smb_error(dgettext(TEXT_DOMAIN,
981 		    "no share name specified for %s@%s"),
982 		    0, ssn->ssn_user, ctx->ct_fullserver);
983 		return (EINVAL);
984 	}
985 	error = nb_ctx_resolve(ctx->ct_nb);
986 	if (error)
987 		return (error);
988 #ifdef KICONV_SUPPORT
989 	if (ssn->ioc_localcs[0] == 0)
990 		strcpy(ssn->ioc_localcs, "default");	/* XXX: locale name ? */
991 	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
992 	if (error)
993 		return (error);
994 	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
995 	if (error)
996 		return (error);
997 	if (ssn->ioc_servercs[0] != 0) {
998 		for (i = 0; i < sizeof (cstbl); i++)
999 			cstbl[i] = i;
1000 		nls_mem_toext(cstbl, cstbl, sizeof (cstbl));
1001 		error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs,
1002 		    cstbl);
1003 		if (error)
1004 			return (error);
1005 		for (i = 0; i < sizeof (cstbl); i++)
1006 			cstbl[i] = i;
1007 		nls_mem_toloc(cstbl, cstbl, sizeof (cstbl));
1008 		error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs,
1009 		    cstbl);
1010 		if (error)
1011 			return (error);
1012 	}
1013 #endif	/* KICONV_SUPPORT */
1014 
1015 	/*
1016 	 * Lookup the IP address.
1017 	 * Puts a list in ct_addrinfo
1018 	 */
1019 	error = smb_ctx_getaddr(ctx);
1020 	if (error) {
1021 		smb_error(dgettext(TEXT_DOMAIN,
1022 		    "can't get server address"), error);
1023 		return (error);
1024 	}
1025 	assert(ctx->ct_addrinfo != NULL);
1026 
1027 	/*
1028 	 * If we have a user name but no password,
1029 	 * check for a keychain entry.
1030 	 * XXX: Only for auth NTLM?
1031 	 */
1032 	if (ctx->ct_user[0] == '\0') {
1033 		/*
1034 		 * No user name (anonymous session).
1035 		 * The minauth checks do not apply.
1036 		 */
1037 		ctx->ct_authflags = SMB_AT_ANON;
1038 	} else {
1039 		/*
1040 		 * Have a user name.
1041 		 * If we don't have a p/w yet,
1042 		 * try the keychain.
1043 		 */
1044 		if (ctx->ct_password[0] == '\0')
1045 			(void) smb_get_keychain(ctx);
1046 		/*
1047 		 * If we're doing p/w based auth,
1048 		 * that means not using Kerberos.
1049 		 */
1050 		if (ctx->ct_password[0] != '\0')
1051 			ctx->ct_authflags &= ~SMB_AT_KRB5;
1052 		/*
1053 		 * Mask out disallowed auth types.
1054 		 */
1055 		ctx->ct_authflags &= ctx->ct_minauth;
1056 	}
1057 	if (ctx->ct_authflags == 0) {
1058 		smb_error(dgettext(TEXT_DOMAIN,
1059 		    "no valid auth. types"), 0);
1060 		return (ENOTSUP);
1061 	}
1062 
1063 	ctx->ct_flags |= SMBCF_RESOLVED;
1064 	if (smb_debug)
1065 		dump_ctx("after smb_ctx_resolve", ctx);
1066 
1067 	return (0);
1068 }
1069 
1070 int
1071 smb_open_driver()
1072 {
1073 	int err, fd;
1074 	uint32_t version;
1075 
1076 	fd = open("/dev/"NSMB_NAME, O_RDWR);
1077 	if (fd < 0) {
1078 		err = errno;
1079 		smb_error(dgettext(TEXT_DOMAIN,
1080 		    "failed to open driver"), err);
1081 		return (-1);
1082 	}
1083 
1084 	/*
1085 	 * Check the driver version (paranoia)
1086 	 * Do this BEFORE any other ioctl calls.
1087 	 */
1088 	if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
1089 		version = 0;
1090 	if (version != NSMB_VERSION) {
1091 		smb_error(dgettext(TEXT_DOMAIN,
1092 		    "incorrect driver version"), 0);
1093 		close(fd);
1094 		return (-1);
1095 	}
1096 
1097 	/* This handle controls per-process resources. */
1098 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
1099 
1100 	return (fd);
1101 }
1102 
1103 int
1104 smb_ctx_gethandle(struct smb_ctx *ctx)
1105 {
1106 	int fd;
1107 
1108 	if (ctx->ct_dev_fd != -1) {
1109 		rpc_cleanup_smbctx(ctx);
1110 		close(ctx->ct_dev_fd);
1111 		ctx->ct_dev_fd = -1;
1112 		ctx->ct_flags &= ~SMBCF_SSNACTIVE;
1113 	}
1114 
1115 	fd = smb_open_driver();
1116 	if (fd < 0)
1117 		return (ENODEV);
1118 
1119 	ctx->ct_dev_fd = fd;
1120 	return (0);
1121 }
1122 
1123 
1124 /*
1125  * Find or create a connection + logon session
1126  */
1127 int
1128 smb_ctx_get_ssn(struct smb_ctx *ctx)
1129 {
1130 	int err = 0;
1131 
1132 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
1133 		return (EINVAL);
1134 
1135 	if (ctx->ct_dev_fd < 0) {
1136 		if ((err = smb_ctx_gethandle(ctx)))
1137 			return (err);
1138 	}
1139 
1140 	/*
1141 	 * Check whether the driver already has a VC
1142 	 * we can use.  If so, we're done!
1143 	 */
1144 	err = smb_ctx_findvc(ctx);
1145 	if (err == 0) {
1146 		DPRINT("found an existing VC");
1147 	} else {
1148 		/*
1149 		 * This calls the IOD to create a new session.
1150 		 */
1151 		DPRINT("setup a new VC");
1152 		err = smb_ctx_newvc(ctx);
1153 		if (err != 0)
1154 			return (err);
1155 
1156 		/*
1157 		 * Call findvc again.  The new VC sould be
1158 		 * found in the driver this time.
1159 		 */
1160 		err = smb_ctx_findvc(ctx);
1161 	}
1162 
1163 	return (err);
1164 }
1165 
1166 /*
1167  * Get the string representation of a share "use" type,
1168  * as needed for the "service" in tree connect.
1169  */
1170 static const char *
1171 smb_use_type_str(smb_use_shtype_t stype)
1172 {
1173 	const char *pp;
1174 
1175 	switch (stype) {
1176 	default:
1177 	case USE_WILDCARD:
1178 		pp = "?????";
1179 		break;
1180 	case USE_DISKDEV:
1181 		pp = "A:";
1182 		break;
1183 	case USE_SPOOLDEV:
1184 		pp = "LPT1:";
1185 		break;
1186 	case USE_CHARDEV:
1187 		pp = "COMM";
1188 		break;
1189 	case USE_IPC:
1190 		pp = "IPC";
1191 		break;
1192 	}
1193 	return (pp);
1194 }
1195 
1196 /*
1197  * Find or create a tree connection
1198  */
1199 int
1200 smb_ctx_get_tree(struct smb_ctx *ctx)
1201 {
1202 	smbioc_tcon_t *tcon = NULL;
1203 	const char *stype;
1204 	int cmd, err = 0;
1205 
1206 	if (ctx->ct_dev_fd < 0 ||
1207 	    ctx->ct_origshare == NULL) {
1208 		return (EINVAL);
1209 	}
1210 
1211 	cmd = SMBIOC_TREE_CONNECT;
1212 	tcon = malloc(sizeof (*tcon));
1213 	if (tcon == NULL)
1214 		return (ENOMEM);
1215 	bzero(tcon, sizeof (*tcon));
1216 	tcon->tc_flags = SMBLK_CREATE;
1217 	tcon->tc_opt = 0;
1218 
1219 	/* The share name */
1220 	strlcpy(tcon->tc_sh.sh_name, ctx->ct_origshare,
1221 	    sizeof (tcon->tc_sh.sh_name));
1222 
1223 	/* The share "use" type. */
1224 	stype = smb_use_type_str(ctx->ct_shtype_req);
1225 	strlcpy(tcon->tc_sh.sh_type_req, stype,
1226 	    sizeof (tcon->tc_sh.sh_type_req));
1227 
1228 	/*
1229 	 * Todo: share passwords for share-level security.
1230 	 *
1231 	 * The driver does the actual TCON call.
1232 	 */
1233 	if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
1234 		err = errno;
1235 		goto out;
1236 	}
1237 
1238 	/*
1239 	 * Check the returned share type
1240 	 */
1241 	DPRINT("ret. sh_type: \"%s\"", tcon->tc_sh.sh_type_ret);
1242 	if (ctx->ct_shtype_req != USE_WILDCARD &&
1243 	    0 != strcmp(stype, tcon->tc_sh.sh_type_ret)) {
1244 		smb_error(dgettext(TEXT_DOMAIN,
1245 		    "%s: incompatible share type"),
1246 		    0, ctx->ct_origshare);
1247 		err = EINVAL;
1248 	}
1249 
1250 out:
1251 	if (tcon != NULL)
1252 		free(tcon);
1253 
1254 	return (err);
1255 }
1256 
1257 /*
1258  * Return the hflags2 word for an smb_ctx.
1259  */
1260 int
1261 smb_ctx_flags2(struct smb_ctx *ctx)
1262 {
1263 	uint16_t flags2;
1264 
1265 	if (ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
1266 		smb_error(dgettext(TEXT_DOMAIN,
1267 		    "can't get flags2 for a session"), errno);
1268 		return (-1);
1269 	}
1270 	return (flags2);
1271 }
1272 
1273 /*
1274  * Get the transport level session key.
1275  * Must already have an active SMB session.
1276  */
1277 int
1278 smb_ctx_get_ssnkey(struct smb_ctx *ctx, uchar_t *key, size_t len)
1279 {
1280 	if (len < SMBIOC_HASH_SZ)
1281 		return (EINVAL);
1282 
1283 	if (ioctl(ctx->ct_dev_fd, SMBIOC_GETSSNKEY, key) == -1)
1284 		return (errno);
1285 
1286 	return (0);
1287 }
1288 
1289 
1290 /*
1291  * RC file parsing stuff
1292  */
1293 
1294 struct nv {
1295 	char *name;
1296 	int value;
1297 } minauth_table[] = {
1298 	/* Allowed auth. types */
1299 	{ "kerberos",	SMB_AT_KRB5 },
1300 	{ "ntlmv2",	SMB_AT_KRB5|SMB_AT_NTLM2 },
1301 	{ "ntlm",	SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1 },
1302 	{ "lm",		SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1 },
1303 	{ "none",	SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1|
1304 			SMB_AT_ANON },
1305 	{ NULL }
1306 };
1307 
1308 
1309 /*
1310  * level values:
1311  * 0 - default
1312  * 1 - server
1313  * 2 - server:user
1314  * 3 - server:user:share
1315  */
1316 static int
1317 smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
1318 {
1319 	char *p;
1320 	int error;
1321 
1322 #ifdef	KICONV_SUPPORT
1323 	if (level > 0) {
1324 		rc_getstringptr(smb_rc, sname, "charsets", &p);
1325 		if (p) {
1326 			error = smb_ctx_setcharset(ctx, p);
1327 			if (error)
1328 				smb_error(dgettext(TEXT_DOMAIN,
1329 	"charset specification in the section '%s' ignored"),
1330 				    error, sname);
1331 		}
1332 	}
1333 #endif
1334 
1335 	if (level <= 1) {
1336 		/* Section is: [default] or [server] */
1337 
1338 		rc_getstringptr(smb_rc, sname, "minauth", &p);
1339 		if (p) {
1340 			/*
1341 			 * "minauth" was set in this section; override
1342 			 * the current minimum authentication setting.
1343 			 */
1344 			struct nv *nvp;
1345 			for (nvp = minauth_table; nvp->name; nvp++)
1346 				if (strcmp(p, nvp->name) == 0)
1347 					break;
1348 			if (nvp->name)
1349 				ctx->ct_minauth = nvp->value;
1350 			else {
1351 				/*
1352 				 * Unknown minimum authentication level.
1353 				 */
1354 				smb_error(dgettext(TEXT_DOMAIN,
1355 "invalid minimum authentication level \"%s\" specified in the section %s"),
1356 				    0, p, sname);
1357 				return (EINVAL);
1358 			}
1359 		}
1360 
1361 		rc_getstringptr(smb_rc, sname, "signing", &p);
1362 		if (p) {
1363 			/*
1364 			 * "signing" was set in this section; override
1365 			 * the current signing settings.
1366 			 */
1367 			ctx->ct_vopt &= ~SMBVOPT_SIGNING_MASK;
1368 			if (strcmp(p, "disabled") == 0) {
1369 				/* leave flags zero (expr for lint) */
1370 				(void) ctx->ct_vopt;
1371 			} else if (strcmp(p, "enabled") == 0) {
1372 				ctx->ct_vopt |=
1373 				    SMBVOPT_SIGNING_ENABLED;
1374 			} else if (strcmp(p, "required") == 0) {
1375 				ctx->ct_vopt |=
1376 				    SMBVOPT_SIGNING_ENABLED |
1377 				    SMBVOPT_SIGNING_REQUIRED;
1378 			} else {
1379 				/*
1380 				 * Unknown "signing" value.
1381 				 */
1382 				smb_error(dgettext(TEXT_DOMAIN,
1383 "invalid signing policy \"%s\" specified in the section %s"),
1384 				    0, p, sname);
1385 				return (EINVAL);
1386 			}
1387 		}
1388 
1389 		/*
1390 		 * Domain name.  Allow both keywords:
1391 		 * "workgroup", "domain"
1392 		 *
1393 		 * Note: these are NOT marked "from CMD".
1394 		 * See long comment at smb_ctx_init()
1395 		 */
1396 		rc_getstringptr(smb_rc, sname, "workgroup", &p);
1397 		if (p) {
1398 			nls_str_upper(p, p);
1399 			error = smb_ctx_setdomain(ctx, p, 0);
1400 			if (error)
1401 				smb_error(dgettext(TEXT_DOMAIN,
1402 				    "workgroup specification in the "
1403 				    "section '%s' ignored"), error, sname);
1404 		}
1405 		rc_getstringptr(smb_rc, sname, "domain", &p);
1406 		if (p) {
1407 			nls_str_upper(p, p);
1408 			error = smb_ctx_setdomain(ctx, p, 0);
1409 			if (error)
1410 				smb_error(dgettext(TEXT_DOMAIN,
1411 				    "domain specification in the "
1412 				    "section '%s' ignored"), error, sname);
1413 		}
1414 
1415 		rc_getstringptr(smb_rc, sname, "user", &p);
1416 		if (p) {
1417 			error = smb_ctx_setuser(ctx, p, 0);
1418 			if (error)
1419 				smb_error(dgettext(TEXT_DOMAIN,
1420 				    "user specification in the "
1421 				    "section '%s' ignored"), error, sname);
1422 		}
1423 	}
1424 
1425 	if (level == 1) {
1426 		/* Section is: [server] */
1427 		rc_getstringptr(smb_rc, sname, "addr", &p);
1428 		if (p) {
1429 			error = smb_ctx_setsrvaddr(ctx, p);
1430 			if (error) {
1431 				smb_error(dgettext(TEXT_DOMAIN,
1432 				    "invalid address specified in section %s"),
1433 				    0, sname);
1434 				return (error);
1435 			}
1436 		}
1437 	}
1438 
1439 	rc_getstringptr(smb_rc, sname, "password", &p);
1440 	if (p) {
1441 		error = smb_ctx_setpassword(ctx, p, 0);
1442 		if (error)
1443 			smb_error(dgettext(TEXT_DOMAIN,
1444 	    "password specification in the section '%s' ignored"),
1445 			    error, sname);
1446 	}
1447 
1448 	return (0);
1449 }
1450 
1451 /*
1452  * read rc file as follows:
1453  * 0: read [default] section
1454  * 1: override with [server] section
1455  * 2: override with [server:user] section
1456  * 3: override with [server:user:share] section
1457  * Since absence of rcfile is not fatal, silently ignore this fact.
1458  * smb_rc file should be closed by caller.
1459  */
1460 int
1461 smb_ctx_readrc(struct smb_ctx *ctx)
1462 {
1463 	char *home;
1464 	char *sname = NULL;
1465 	int sname_max;
1466 	int err = 0;
1467 
1468 	if ((home = getenv("HOME")) == NULL)
1469 		home = ctx->ct_home;
1470 	if ((err = smb_open_rcfile(home)) != 0) {
1471 		DPRINT("smb_open_rcfile, err=%d", err);
1472 		/* ignore any error here */
1473 		return (0);
1474 	}
1475 
1476 	sname_max = 3 * SMBIOC_MAX_NAME + 4;
1477 	sname = malloc(sname_max);
1478 	if (sname == NULL) {
1479 		err = ENOMEM;
1480 		goto done;
1481 	}
1482 
1483 	/*
1484 	 * default parameters (level=0)
1485 	 */
1486 	smb_ctx_readrcsection(ctx, "default", 0);
1487 	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
1488 
1489 	/*
1490 	 * If we don't have a server name, we can't read any of the
1491 	 * [server...] sections.
1492 	 */
1493 	if (ctx->ct_fullserver == NULL)
1494 		goto done;
1495 	/*
1496 	 * SERVER parameters.
1497 	 */
1498 	smb_ctx_readrcsection(ctx, ctx->ct_fullserver, 1);
1499 
1500 	/*
1501 	 * If we don't have a user name, we can't read any of the
1502 	 * [server:user...] sections.
1503 	 */
1504 	if (ctx->ct_user[0] == 0)
1505 		goto done;
1506 	/*
1507 	 * SERVER:USER parameters
1508 	 */
1509 	snprintf(sname, sname_max, "%s:%s",
1510 	    ctx->ct_fullserver,
1511 	    ctx->ct_user);
1512 	smb_ctx_readrcsection(ctx, sname, 2);
1513 
1514 
1515 	/*
1516 	 * If we don't have a share name, we can't read any of the
1517 	 * [server:user:share] sections.
1518 	 */
1519 	if (ctx->ct_origshare == NULL)
1520 		goto done;
1521 	/*
1522 	 * SERVER:USER:SHARE parameters
1523 	 */
1524 	snprintf(sname, sname_max, "%s:%s:%s",
1525 	    ctx->ct_fullserver,
1526 	    ctx->ct_user,
1527 	    ctx->ct_origshare);
1528 	smb_ctx_readrcsection(ctx, sname, 3);
1529 
1530 done:
1531 	if (sname)
1532 		free(sname);
1533 	smb_close_rcfile();
1534 	if (smb_debug)
1535 		dump_ctx("after smb_ctx_readrc", ctx);
1536 	if (err)
1537 		DPRINT("err=%d\n", err);
1538 
1539 	return (err);
1540 }
1541