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