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