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