xref: /illumos-gate/usr/src/lib/libsmbfs/smb/ctx.c (revision b76c1459)
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 2008 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 <kerberosv5/krb5.h>
62 #include <kerberosv5/com_err.h>
63 
64 #include <netsmb/smb_lib.h>
65 #include <netsmb/netbios.h>
66 #include <netsmb/nb_lib.h>
67 #include <netsmb/smb_dev.h>
68 #include <cflib.h>
69 #include <charsets.h>
70 
71 #include <spnego.h>
72 #include "derparse.h"
73 #include "private.h"
74 
75 extern MECH_OID g_stcMechOIDList [];
76 
77 #define	POWEROF2(x) (((x) & ((x)-1)) == 0)
78 
79 /* These two may be set by commands. */
80 int smb_debug, smb_verbose;
81 
82 /*
83  * Give the RPC library a callback hook that will be
84  * called whenever we destroy or reinit an smb_ctx_t.
85  * The name rpc_cleanup_smbctx() is legacy, and was
86  * originally a direct call into the RPC code.
87  */
88 static smb_ctx_close_hook_t close_hook;
89 static void
90 rpc_cleanup_smbctx(struct smb_ctx *ctx)
91 {
92 	if (close_hook)
93 		(*close_hook)(ctx);
94 }
95 void
96 smb_ctx_set_close_hook(smb_ctx_close_hook_t hook)
97 {
98 	close_hook = hook;
99 }
100 
101 void
102 dump_ctx_flags(int flags)
103 {
104 	printf(" Flags: ");
105 	if (flags == 0)
106 		printf("0");
107 	if (flags & SMBCF_NOPWD)
108 		printf("NOPWD ");
109 	if (flags & SMBCF_SRIGHTS)
110 		printf("SRIGHTS ");
111 	if (flags & SMBCF_LOCALE)
112 		printf("LOCALE ");
113 	if (flags & SMBCF_CMD_DOM)
114 		printf("CMD_DOM ");
115 	if (flags & SMBCF_CMD_USR)
116 		printf("CMD_USR ");
117 	if (flags & SMBCF_CMD_PW)
118 		printf("CMD_PW ");
119 	if (flags & SMBCF_RESOLVED)
120 		printf("RESOLVED ");
121 	if (flags & SMBCF_KCBAD)
122 		printf("KCBAD ");
123 	if (flags & SMBCF_KCFOUND)
124 		printf("KCFOUND ");
125 	if (flags & SMBCF_BROWSEOK)
126 		printf("BROWSEOK ");
127 	if (flags & SMBCF_AUTHREQ)
128 		printf("AUTHREQ ");
129 	if (flags & SMBCF_KCSAVE)
130 		printf("KCSAVE  ");
131 	if (flags & SMBCF_XXX)
132 		printf("XXX ");
133 	if (flags & SMBCF_SSNACTIVE)
134 		printf("SSNACTIVE ");
135 	if (flags & SMBCF_KCDOMAIN)
136 		printf("KCDOMAIN ");
137 	printf("\n");
138 }
139 
140 void
141 dump_ctx_ssn(struct smbioc_ossn *ssn)
142 {
143 	printf(" srvname=\"%s\", dom=\"%s\", user=\"%s\", password=%s\n",
144 	    ssn->ioc_srvname, ssn->ioc_workgroup, ssn->ioc_user,
145 	    ssn->ioc_password[0] ? "(non-null)" : "NULL");
146 	printf(" timeout=%d, retry=%d, owner=%d, group=%d\n",
147 	    ssn->ioc_timeout, ssn->ioc_retrycount,
148 	    ssn->ioc_owner, ssn->ioc_group);
149 }
150 
151 void
152 dump_ctx_sh(struct smbioc_oshare *sh)
153 {
154 	printf(" share_name=\"%s\", share_pw=\"%s\"\n",
155 	    sh->ioc_share, sh->ioc_password);
156 }
157 
158 void
159 dump_ctx(char *where, struct smb_ctx *ctx)
160 {
161 	printf("context %s:\n", where);
162 	dump_ctx_flags(ctx->ct_flags);
163 
164 	printf(" localname=\"%s\"", ctx->ct_locname);
165 
166 	if (ctx->ct_fullserver)
167 		printf(" fullserver=\"%s\"", ctx->ct_fullserver);
168 	else
169 		printf(" fullserver=NULL");
170 
171 	if (ctx->ct_srvaddr)
172 		printf(" srvaddr=\"%s\"\n", ctx->ct_srvaddr);
173 	else
174 		printf(" srvaddr=NULL\n");
175 
176 	dump_ctx_ssn(&ctx->ct_ssn);
177 	dump_ctx_sh(&ctx->ct_sh);
178 }
179 
180 /*
181  * Initialize an smb_ctx struct.
182  *
183  * The sequence for getting all the members filled in
184  * has some tricky aspects.  Here's how it works:
185  *
186  * The search order for options is as follows:
187  *   command line options
188  *   values parsed from UNC path (cmd)
189  *   values from RC file (per-user)
190  *   values from SMF (system-wide)
191  *   built-in defaults
192  *
193  * Normally, one would simply get all the values starting with
194  * the bottom of the above list and working to the top, and
195  * overwriting values as you go.  But we need an exception.
196  *
197  * In this function, we parse the UNC path and command line options,
198  * because we need (at least) the server name when we're getting the
199  * SMF and RC file values.  However, values we get from the command
200  * should not be overwritten by SMF or RC file parsing, so we mark
201  * values from the command as "from CMD" and the RC file parser
202  * leaves in place any values so marked.  See: SMBCF_CMD_*
203  *
204  * The semantics of these flags are: "This value came from the
205  * current command instance, not from sources that may apply to
206  * multiple commands."  (Different from the old "FROMUSR" flag.)
207  *
208  * Note that smb_ctx_opt() is called later to handle the
209  * remaining options, which should be ignored here.
210  * The (magic) leading ":" in cf_getopt() makes it
211  * ignore options not in the options string.
212  */
213 int
214 smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
215 	int minlevel, int maxlevel, int sharetype)
216 {
217 	int  opt, error = 0;
218 	const char *arg, *cp;
219 	struct passwd pw;
220 	char pwbuf[NSS_BUFLEN_PASSWD];
221 	int aflg = 0, uflg = 0;
222 
223 	bzero(ctx, sizeof (*ctx));
224 	if (sharetype == SMB_ST_DISK)
225 		ctx->ct_flags |= SMBCF_BROWSEOK;
226 	error = nb_ctx_create(&ctx->ct_nb);
227 	if (error)
228 		return (error);
229 
230 	ctx->ct_fd = -1;
231 	ctx->ct_parsedlevel = SMBL_NONE;
232 	ctx->ct_minlevel = minlevel;
233 	ctx->ct_maxlevel = maxlevel;
234 
235 	/* Fill in defaults */
236 	ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE | SMBVOPT_MINAUTH_NTLM;
237 
238 	ctx->ct_ssn.ioc_timeout = 15;
239 	ctx->ct_ssn.ioc_retrycount = 4;
240 	ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
241 	ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
242 	ctx->ct_ssn.ioc_mode = SMBM_EXEC;
243 	ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
244 
245 	ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
246 	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
247 	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
248 	ctx->ct_sh.ioc_mode = SMBM_EXEC;
249 	ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
250 	ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
251 	ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
252 
253 	nb_ctx_setscope(ctx->ct_nb, "");
254 
255 	/*
256 	 * if the user name is not specified some other way,
257 	 * use the current user name (built-in default)
258 	 */
259 	if (getpwuid_r(geteuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL)
260 		smb_ctx_setuser(ctx, pw.pw_name, 0);
261 
262 	/*
263 	 * Set a built-in default domain (workgroup).
264 	 * XXX: What's the best default? Use "?" instead?
265 	 * Using the Windows/NT default for now.
266 	 */
267 	smb_ctx_setworkgroup(ctx, "WORKGROUP", 0);
268 
269 	/*
270 	 * Parse the UNC path.  Values from here are
271 	 * marked as "from CMD".
272 	 */
273 	if (argv == NULL)
274 		goto done;
275 	for (opt = 1; opt < argc; opt++) {
276 		cp = argv[opt];
277 		if (strncmp(cp, "//", 2) != 0)
278 			continue;
279 		error = smb_ctx_parseunc(ctx, cp, sharetype, &cp);
280 		if (error)
281 			return (error);
282 		break;
283 	}
284 
285 	/*
286 	 * Parse options, if any.  Values from here too
287 	 * are marked as "from CMD".
288 	 */
289 	while (error == 0 && (opt = cf_getopt(argc, argv, ":AU:E:L:")) != -1) {
290 		arg = cf_optarg;
291 		switch (opt) {
292 		case 'A':
293 			aflg = 1;
294 			error = smb_ctx_setuser(ctx, "", TRUE);
295 			error = smb_ctx_setpassword(ctx, "", TRUE);
296 			ctx->ct_flags |= SMBCF_NOPWD;
297 			break;
298 		case 'E':
299 #if 0 /* We don't support any "charset" stuff. (ignore -E) */
300 			error = smb_ctx_setcharset(ctx, arg);
301 			if (error)
302 				return (error);
303 #endif
304 			break;
305 		case 'L':
306 #if 0 /* Use the standard environment variables (ignore -L) */
307 			error = nls_setlocale(optarg);
308 			if (error)
309 				break;
310 #endif
311 			break;
312 		case 'U':
313 			uflg = 1;
314 			error = smb_ctx_setuser(ctx, arg, TRUE);
315 			break;
316 		}
317 	}
318 	if (aflg && uflg)  {
319 		printf(gettext("-A and -U flags are exclusive.\n"));
320 		return (1);
321 	}
322 	cf_optind = cf_optreset = 1;
323 
324 done:
325 	if (smb_debug)
326 		dump_ctx("after smb_ctx_init", ctx);
327 
328 	return (error);
329 }
330 
331 void
332 smb_ctx_done(struct smb_ctx *ctx)
333 {
334 
335 	rpc_cleanup_smbctx(ctx);
336 
337 	/* Kerberos stuff.  See smb_ctx_krb5init() */
338 	if (ctx->ct_krb5ctx) {
339 		if (ctx->ct_krb5cp)
340 			krb5_free_principal(ctx->ct_krb5ctx, ctx->ct_krb5cp);
341 		krb5_free_context(ctx->ct_krb5ctx);
342 	}
343 
344 	if (ctx->ct_fd != -1)
345 		close(ctx->ct_fd);
346 #if 0 /* XXX: not pointers anymore */
347 	if (&ctx->ct_ssn.ioc_server)
348 		nb_snbfree(&ctx->ct_ssn.ioc_server);
349 	if (&ctx->ct_ssn.ioc_local)
350 		nb_snbfree(&ctx->ct_ssn.ioc_local);
351 #endif
352 	if (ctx->ct_srvaddr)
353 		free(ctx->ct_srvaddr);
354 	if (ctx->ct_nb)
355 		nb_ctx_done(ctx->ct_nb);
356 	if (ctx->ct_secblob)
357 		free(ctx->ct_secblob);
358 	if (ctx->ct_origshare)
359 		free(ctx->ct_origshare);
360 	if (ctx->ct_fullserver)
361 		free(ctx->ct_fullserver);
362 }
363 
364 static int
365 getsubstring(const char *p, uchar_t sep, char *dest, int maxlen,
366     const char **next)
367 {
368 	int len;
369 
370 	maxlen--;
371 	for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
372 		if (*p == 0)
373 			return (EINVAL);
374 		*dest = *p;
375 	}
376 	*dest = 0;
377 	*next = *p ? p + 1 : p;
378 	return (0);
379 }
380 
381 /*
382  * Parse the UNC path.  Here we expect something like
383  *   "//[workgroup;][user[:password]@]host[/share[/path]]"
384  * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
385  * Values found here are marked as "from CMD".
386  */
387 int
388 smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
389 	const char **next)
390 {
391 	const char *p = unc;
392 	char *p1, *colon, *servername;
393 	char tmp[1024];
394 	char tmp2[1024];
395 	int error;
396 
397 	ctx->ct_parsedlevel = SMBL_NONE;
398 	if (*p++ != '/' || *p++ != '/') {
399 		smb_error(dgettext(TEXT_DOMAIN,
400 		    "UNC should start with '//'"), 0);
401 		return (EINVAL);
402 	}
403 	p1 = tmp;
404 	error = getsubstring(p, ';', p1, sizeof (tmp), &p);
405 	if (!error) {
406 		if (*p1 == 0) {
407 			smb_error(dgettext(TEXT_DOMAIN,
408 			    "empty workgroup name"), 0);
409 			return (EINVAL);
410 		}
411 		nls_str_upper(tmp, tmp);
412 		error = smb_ctx_setworkgroup(ctx, unpercent(tmp), TRUE);
413 		if (error)
414 			return (error);
415 	}
416 	colon = (char *)p;
417 	error = getsubstring(p, '@', p1, sizeof (tmp), &p);
418 	if (!error) {
419 		if (ctx->ct_maxlevel < SMBL_VC) {
420 			smb_error(dgettext(TEXT_DOMAIN,
421 			    "no user name required"), 0);
422 			return (EINVAL);
423 		}
424 		p1 = strchr(tmp, ':');
425 		if (p1) {
426 			colon += p1 - tmp;
427 			*p1++ = (char)0;
428 			error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE);
429 			if (error)
430 				return (error);
431 			if (p - colon > 2)
432 				memset(colon+1, '*', p - colon - 2);
433 		}
434 		p1 = tmp;
435 		if (*p1 == 0) {
436 			smb_error(dgettext(TEXT_DOMAIN,
437 			    "empty user name"), 0);
438 			return (EINVAL);
439 		}
440 		error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE);
441 		if (error)
442 			return (error);
443 		ctx->ct_parsedlevel = SMBL_VC;
444 	}
445 	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
446 	if (error) {
447 		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
448 		if (error) {
449 			smb_error(dgettext(TEXT_DOMAIN,
450 			    "no server name found"), 0);
451 			return (error);
452 		}
453 	}
454 	if (*p1 == 0) {
455 		smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
456 		return (EINVAL);
457 	}
458 
459 
460 	/*
461 	 * It's safe to uppercase this string, which
462 	 * consists of ascii characters that should
463 	 * be uppercased, %s, and ascii characters representing
464 	 * hex digits 0-9 and A-F (already uppercased, and
465 	 * if not uppercased they need to be). However,
466 	 * it is NOT safe to uppercase after it has been
467 	 * converted, below!
468 	 */
469 
470 	nls_str_upper(tmp2, tmp);
471 
472 	/*
473 	 * scan for % in the string.
474 	 * If we find one, convert
475 	 * to the assumed codepage.
476 	 */
477 
478 	if (strchr(tmp2, '%')) {
479 		/* use the 1st buffer, we don't need the old string */
480 		servername = tmp;
481 		if (!(servername = convert_utf8_to_wincs(unpercent(tmp2)))) {
482 			smb_error(dgettext(TEXT_DOMAIN, "bad server name"), 0);
483 			return (EINVAL);
484 		}
485 		/*
486 		 * Converts utf8 to win equivalent of
487 		 * what is configured on this machine.
488 		 * Note that we are assuming this is the
489 		 * encoding used on the server, and that
490 		 * assumption might be incorrect. This is
491 		 * the best we can do now, and we should
492 		 * move to use port 445 to avoid having
493 		 * to worry about server codepages.
494 		 */
495 	} else /* no conversion needed */
496 		servername = tmp2;
497 
498 	smb_ctx_setserver(ctx, servername);
499 	error = smb_ctx_setfullserver(ctx, servername);
500 
501 	if (error)
502 		return (error);
503 	if (sharetype == SMB_ST_NONE) {
504 		*next = p;
505 		return (0);
506 	}
507 	if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
508 		smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0);
509 		return (EINVAL);
510 	}
511 	error = getsubstring(p, '/', p1, sizeof (tmp), &p);
512 	if (error) {
513 		error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
514 		if (error) {
515 			smb_error(dgettext(TEXT_DOMAIN,
516 			    "unexpected end of line"), 0);
517 			return (error);
518 		}
519 	}
520 	if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE &&
521 	    !(ctx->ct_flags & SMBCF_BROWSEOK)) {
522 		smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
523 		return (EINVAL);
524 	}
525 	*next = p;
526 	if (*p1 == 0)
527 		return (0);
528 	error = smb_ctx_setshare(ctx, unpercent(p1), sharetype);
529 	return (error);
530 }
531 
532 int
533 smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
534 {
535 	char *cp, *servercs, *localcs;
536 	int cslen = sizeof (ctx->ct_ssn.ioc_localcs);
537 	int scslen, lcslen, error;
538 
539 	cp = strchr(arg, ':');
540 	lcslen = cp ? (cp - arg) : 0;
541 	if (lcslen == 0 || lcslen >= cslen) {
542 		smb_error(dgettext(TEXT_DOMAIN,
543 		    "invalid local charset specification (%s)"), 0, arg);
544 		return (EINVAL);
545 	}
546 	scslen = (size_t)strlen(++cp);
547 	if (scslen == 0 || scslen >= cslen) {
548 		smb_error(dgettext(TEXT_DOMAIN,
549 		    "invalid server charset specification (%s)"), 0, arg);
550 		return (EINVAL);
551 	}
552 	localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
553 	localcs[lcslen] = 0;
554 	servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
555 	error = nls_setrecode(localcs, servercs);
556 	if (error == 0)
557 		return (0);
558 	smb_error(dgettext(TEXT_DOMAIN,
559 	    "can't initialize iconv support (%s:%s)"),
560 	    error, localcs, servercs);
561 	localcs[0] = 0;
562 	servercs[0] = 0;
563 	return (error);
564 }
565 
566 int
567 smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
568 {
569 	ctx->ct_fullserver = strdup(name);
570 	if (ctx->ct_fullserver == NULL)
571 		return (ENOMEM);
572 	return (0);
573 }
574 
575 /*
576  * XXX TODO FIXME etc etc
577  * If the call to nbns_getnodestatus(...) fails we can try one of two other
578  * methods; use a name of "*SMBSERVER", which is supported by Samba (at least)
579  * or, as a last resort, try the "truncate-at-dot" heuristic.
580  * And the heuristic really should attempt truncation at
581  * each dot in turn, left to right.
582  *
583  * These fallback heuristics should be triggered when the attempt to open the
584  * session fails instead of in the code below.
585  *
586  * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
587  */
588 int
589 smb_ctx_getnbname(struct smb_ctx *ctx, struct sockaddr *sap)
590 {
591 	char server[SMB_MAXSRVNAMELEN + 1];
592 	char workgroup[SMB_MAXUSERNAMELEN + 1];
593 	int error;
594 #if 0
595 	char *dot;
596 #endif
597 
598 	server[0] = workgroup[0] = '\0';
599 	error = nbns_getnodestatus(sap, ctx->ct_nb, server, workgroup);
600 	if (error == 0) {
601 		/*
602 		 * Used to set our domain name to be the same as
603 		 * the server's domain name.   Unnecessary at best,
604 		 * and wrong for accounts in a trusted domain.
605 		 */
606 #ifdef APPLE
607 		if (workgroup[0] && !ctx->ct_ssn.ioc_workgroup[0])
608 			smb_ctx_setworkgroup(ctx, workgroup, 0);
609 #endif
610 		if (server[0])
611 			smb_ctx_setserver(ctx, server);
612 	} else {
613 		if (smb_verbose)
614 			smb_error(dgettext(TEXT_DOMAIN,
615 			    "Failed to get NetBIOS node status."), 0);
616 		if (ctx->ct_ssn.ioc_srvname[0] == (char)0)
617 			smb_ctx_setserver(ctx, "*SMBSERVER");
618 	}
619 #if 0
620 	if (server[0] == (char)0) {
621 		dot = strchr(ctx->ct_fullserver, '.');
622 		if (dot)
623 			*dot = '\0';
624 		if (strlen(ctx->ct_fullserver) <= SMB_MAXSRVNAMELEN) {
625 			/*
626 			 * don't uppercase the server name. it comes from
627 			 * NBNS and uppercasing can clobber the characters
628 			 */
629 			strcpy(ctx->ct_ssn.ioc_srvname, ctx->ct_fullserver);
630 			error = 0;
631 		} else {
632 			error = -1;
633 		}
634 		if (dot)
635 			*dot = '.';
636 	}
637 #endif
638 	return (error);
639 }
640 
641 /* this routine does not uppercase the server name */
642 void
643 smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
644 {
645 	/* don't uppercase the server name */
646 	if (strlen(name) > SMB_MAXSRVNAMELEN) { /* NB limit is 15 */
647 		ctx->ct_ssn.ioc_srvname[0] = '\0';
648 	} else
649 		strcpy(ctx->ct_ssn.ioc_srvname, name);
650 }
651 
652 int
653 smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
654 {
655 
656 	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
657 		smb_error(dgettext(TEXT_DOMAIN,
658 		    "user name '%s' too long"), 0, name);
659 		return (ENAMETOOLONG);
660 	}
661 
662 	/*
663 	 * Don't overwrite a value from the command line
664 	 * with one from anywhere else.
665 	 */
666 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR))
667 		return (0);
668 
669 	/* don't uppercase the username, just copy it. */
670 	strcpy(ctx->ct_ssn.ioc_user, name);
671 
672 	/* Mark this as "from the command line". */
673 	if (from_cmd)
674 		ctx->ct_flags |= SMBCF_CMD_USR;
675 
676 	return (0);
677 }
678 
679 /*
680  * Never uppercase the workgroup
681  * name here, because it might come
682  * from a Windows codepage encoding.
683  *
684  * Don't overwrite a domain name from the
685  * command line with one from anywhere else.
686  * See smb_ctx_init() for notes about this.
687  */
688 int
689 smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd)
690 {
691 
692 	if (strlen(name) >= SMB_MAXUSERNAMELEN) {
693 		smb_error(dgettext(TEXT_DOMAIN,
694 		    "workgroup name '%s' too long"), 0, name);
695 		return (ENAMETOOLONG);
696 	}
697 
698 	/*
699 	 * Don't overwrite a value from the command line
700 	 * with one from anywhere else.
701 	 */
702 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
703 		return (0);
704 
705 	strcpy(ctx->ct_ssn.ioc_workgroup, name);
706 
707 	/* Mark this as "from the command line". */
708 	if (from_cmd)
709 		ctx->ct_flags |= SMBCF_CMD_DOM;
710 
711 	return (0);
712 }
713 
714 int
715 smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
716 {
717 
718 	if (passwd == NULL) /* XXX Huh? */
719 		return (EINVAL);
720 	if (strlen(passwd) >= SMB_MAXPASSWORDLEN) {
721 		smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
722 		return (ENAMETOOLONG);
723 	}
724 
725 	/*
726 	 * Don't overwrite a value from the command line
727 	 * with one from anywhere else.
728 	 */
729 	if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW))
730 		return (0);
731 
732 	if (strncmp(passwd, "$$1", 3) == 0)
733 		smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
734 	else
735 		strcpy(ctx->ct_ssn.ioc_password, passwd);
736 	strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
737 
738 	/* Mark this as "from the command line". */
739 	if (from_cmd)
740 		ctx->ct_flags |= SMBCF_CMD_PW;
741 
742 	return (0);
743 }
744 
745 int
746 smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
747 {
748 	if (strlen(share) >= SMB_MAXSHARENAMELEN) {
749 		smb_error(dgettext(TEXT_DOMAIN,
750 		    "share name '%s' too long"), 0, share);
751 		return (ENAMETOOLONG);
752 	}
753 	if (ctx->ct_origshare)
754 		free(ctx->ct_origshare);
755 	if ((ctx->ct_origshare = strdup(share)) == NULL)
756 		return (ENOMEM);
757 	nls_str_upper(ctx->ct_sh.ioc_share, share);
758 	if (share[0] != 0)
759 		ctx->ct_parsedlevel = SMBL_SHARE;
760 	ctx->ct_sh.ioc_stype = stype;
761 	return (0);
762 }
763 
764 int
765 smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
766 {
767 	if (addr == NULL || addr[0] == 0)
768 		return (EINVAL);
769 	if (ctx->ct_srvaddr)
770 		free(ctx->ct_srvaddr);
771 	if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
772 		return (ENOMEM);
773 	return (0);
774 }
775 
776 static int
777 smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
778 {
779 	struct group gr;
780 	struct passwd pw;
781 	char buf[NSS_BUFLEN_PASSWD];
782 	char *cp;
783 
784 	cp = strchr(pair, ':');
785 	if (cp) {
786 		*cp++ = '\0';
787 		if (*cp) {
788 			if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) {
789 				*gid = gr.gr_gid;
790 			} else
791 				smb_error(dgettext(TEXT_DOMAIN,
792 				    "Invalid group name %s, ignored"), 0, cp);
793 		}
794 	}
795 	if (*pair) {
796 		if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) {
797 			*uid = pw.pw_uid;
798 		} else
799 			smb_error(dgettext(TEXT_DOMAIN,
800 			    "Invalid user name %s, ignored"), 0, pair);
801 	}
802 
803 	return (0);
804 }
805 
806 /*
807  * Commands use this with getopt.  See:
808  *   STDPARAM_OPT, STDPARAM_ARGS
809  * Called after smb_ctx_readrc().
810  */
811 int
812 smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
813 {
814 	int error = 0;
815 	char *p, *cp;
816 	char tmp[1024];
817 
818 	switch (opt) {
819 	case 'A':
820 	case 'U':
821 		/* Handled in smb_ctx_init() */
822 		break;
823 	case 'I':
824 		error = smb_ctx_setsrvaddr(ctx, arg);
825 		break;
826 	case 'M':
827 		ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
828 		if (*cp == '/') {
829 			ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
830 			ctx->ct_flags |= SMBCF_SRIGHTS;
831 		}
832 		break;
833 	case 'N':
834 		ctx->ct_flags |= SMBCF_NOPWD;
835 		break;
836 	case 'O':
837 		p = strdup(arg);
838 		cp = strchr(p, '/');
839 		if (cp) {
840 			*cp++ = '\0';
841 			error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
842 			    &ctx->ct_sh.ioc_group);
843 		}
844 		if (*p && error == 0) {
845 			error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner,
846 			    &ctx->ct_ssn.ioc_group);
847 		}
848 		free(p);
849 		break;
850 	case 'P':
851 /*		ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT; */
852 		break;
853 	case 'R':
854 		ctx->ct_ssn.ioc_retrycount = atoi(arg);
855 		break;
856 	case 'T':
857 		ctx->ct_ssn.ioc_timeout = atoi(arg);
858 		break;
859 	case 'W':
860 		nls_str_upper(tmp, arg);
861 		error = smb_ctx_setworkgroup(ctx, tmp, TRUE);
862 		break;
863 	}
864 	return (error);
865 }
866 
867 #if 0
868 static void
869 smb_hexdump(const uchar_t *buf, int len) {
870 	int ofs = 0;
871 
872 	while (len--) {
873 		if (ofs % 16 == 0)
874 			printf("\n%02X: ", ofs);
875 		printf("%02x ", *buf++);
876 		ofs++;
877 	}
878 	printf("\n");
879 }
880 #endif
881 
882 
883 static int
884 smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl)
885 {
886 	int error;
887 
888 	/*
889 	 * Not able to find out what is the work of this routine till
890 	 * now. Still investigating.
891 	 * REVISIT
892 	 */
893 #ifdef KICONV_SUPPORT
894 	error = kiconv_add_xlat_table(to, from, tbl);
895 	if (error && error != EEXIST) {
896 		smb_error(dgettext(TEXT_DOMAIN,
897 		    "can not setup kernel iconv table (%s:%s)"),
898 		    error, from, to);
899 		return (error);
900 	}
901 #endif
902 	return (0);
903 }
904 
905 /*
906  * Verify context before connect operation(s),
907  * lookup specified server and try to fill all forgotten fields.
908  */
909 int
910 smb_ctx_resolve(struct smb_ctx *ctx)
911 {
912 	struct smbioc_ossn *ssn = &ctx->ct_ssn;
913 	struct smbioc_oshare *sh = &ctx->ct_sh;
914 	struct nb_name nn;
915 	struct sockaddr *sap;
916 	struct sockaddr_nb *salocal, *saserver;
917 	char *cp;
918 	uchar_t cstbl[256];
919 	uint_t i;
920 	int error = 0;
921 	int browseok = ctx->ct_flags & SMBCF_BROWSEOK;
922 	int renego = 0;
923 
924 	ctx->ct_flags &= ~SMBCF_RESOLVED;
925 	if (isatty(STDIN_FILENO))
926 		browseok = 0;
927 	if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == 0) {
928 		smb_error(dgettext(TEXT_DOMAIN,
929 		    "no server name specified"), 0);
930 		return (EINVAL);
931 	}
932 	if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0 &&
933 	    !browseok) {
934 		smb_error(dgettext(TEXT_DOMAIN,
935 		    "no share name specified for %s@%s"),
936 		    0, ssn->ioc_user, ssn->ioc_srvname);
937 		return (EINVAL);
938 	}
939 	error = nb_ctx_resolve(ctx->ct_nb);
940 	if (error)
941 		return (error);
942 	if (ssn->ioc_localcs[0] == 0)
943 		strcpy(ssn->ioc_localcs, "default");	/* XXX: locale name ? */
944 	error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
945 	if (error)
946 		return (error);
947 	error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
948 	if (error)
949 		return (error);
950 	if (ssn->ioc_servercs[0] != 0) {
951 		for (i = 0; i < sizeof (cstbl); i++)
952 			cstbl[i] = i;
953 		nls_mem_toext(cstbl, cstbl, sizeof (cstbl));
954 		error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs,
955 		    cstbl);
956 		if (error)
957 			return (error);
958 		for (i = 0; i < sizeof (cstbl); i++)
959 			cstbl[i] = i;
960 		nls_mem_toloc(cstbl, cstbl, sizeof (cstbl));
961 		error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs,
962 		    cstbl);
963 		if (error)
964 			return (error);
965 	}
966 	/*
967 	 * If we have an explicit address set for the server in
968 	 * an "addr=X" setting in .nsmbrc or SMF, just try using a
969 	 * gethostbyname() lookup for it.
970 	 */
971 	if (ctx->ct_srvaddr) {
972 		error = nb_resolvehost_in(ctx->ct_srvaddr, &sap);
973 		if (error == 0)
974 			(void) smb_ctx_getnbname(ctx, sap);
975 	} else
976 		error = -1;
977 
978 	/*
979 	 * Next try a gethostbyname() lookup on the original user-
980 	 * specified server name. This is similar to Windows
981 	 * NBT option "Use DNS for name resolution."
982 	 */
983 	if (error && ctx->ct_fullserver) {
984 		error = nb_resolvehost_in(ctx->ct_fullserver, &sap);
985 		if (error == 0)
986 			(void) smb_ctx_getnbname(ctx, sap);
987 	}
988 
989 	/*
990 	 * Finally, try the shorter, upper-cased ssn->ioc_srvname
991 	 * with a NBNS/WINS lookup if the "nbns_enable" property is
992 	 * true (the default).  nbns_resolvename() may unicast to the
993 	 * "nbns" server or broadcast on the subnet.
994 	 */
995 	if (error && ssn->ioc_srvname[0] &&
996 	    ctx->ct_nb->nb_flags & NBCF_NS_ENABLE) {
997 		error = nbns_resolvename(ssn->ioc_srvname,
998 		    ctx->ct_nb, &sap);
999 		/*
1000 		 * Used to get the NetBIOS node status here.
1001 		 * Not necessary (we have the NetBIOS name).
1002 		 */
1003 	}
1004 	if (error) {
1005 		smb_error(dgettext(TEXT_DOMAIN,
1006 		    "can't get server address"), error);
1007 		return (error);
1008 	}
1009 
1010 	/* XXX: no nls_str_upper(ssn->ioc_srvname) here? */
1011 
1012 	assert(sizeof (nn.nn_name) == sizeof (ssn->ioc_srvname));
1013 	memcpy(nn.nn_name, ssn->ioc_srvname, NB_NAMELEN);
1014 	nn.nn_type = NBT_SERVER;
1015 	nn.nn_scope = ctx->ct_nb->nb_scope;
1016 
1017 	error = nb_sockaddr(sap, &nn, &saserver);
1018 	memcpy(&ctx->ct_srvinaddr, sap, sizeof (struct sockaddr_in));
1019 	nb_snbfree(sap);
1020 	if (error) {
1021 		smb_error(dgettext(TEXT_DOMAIN,
1022 		    "can't allocate server address"), error);
1023 		return (error);
1024 	}
1025 	/* We know it's a NetBIOS address here. */
1026 	bcopy(saserver, &ssn->ioc_server.nb,
1027 	    sizeof (struct sockaddr_nb));
1028 	if (ctx->ct_locname[0] == 0) {
1029 		error = nb_getlocalname(ctx->ct_locname,
1030 		    SMB_MAXUSERNAMELEN + 1);
1031 		if (error) {
1032 			smb_error(dgettext(TEXT_DOMAIN,
1033 			    "can't get local name"), error);
1034 			return (error);
1035 		}
1036 		nls_str_upper(ctx->ct_locname, ctx->ct_locname);
1037 	}
1038 
1039 	/* XXX: no nls_str_upper(ctx->ct_locname); here? */
1040 
1041 	memcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN);
1042 	nn.nn_type = NBT_WKSTA;
1043 	nn.nn_scope = ctx->ct_nb->nb_scope;
1044 
1045 	error = nb_sockaddr(NULL, &nn, &salocal);
1046 	if (error) {
1047 		nb_snbfree((struct sockaddr *)saserver);
1048 		smb_error(dgettext(TEXT_DOMAIN,
1049 		    "can't allocate local address"), error);
1050 		return (error);
1051 	}
1052 
1053 	/* We know it's a NetBIOS address here. */
1054 	bcopy(salocal, &ssn->ioc_local.nb,
1055 	    sizeof (struct sockaddr_nb));
1056 
1057 	error = smb_ctx_findvc(ctx, SMBL_VC, 0);
1058 	if (error == 0) {
1059 		/* re-use and existing VC */
1060 		ctx->ct_flags |= SMBCF_RESOLVED | SMBCF_SSNACTIVE;
1061 		return (0);
1062 	}
1063 
1064 	/* Make a new connection via smb_ctx_negotiate()... */
1065 	error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE,
1066 	    ssn->ioc_workgroup);
1067 	if (error)
1068 		return (error);
1069 	ctx->ct_flags &= ~SMBCF_AUTHREQ;
1070 	if (!ctx->ct_secblob && browseok && !sh->ioc_share[0] &&
1071 	    !(ctx->ct_flags & SMBCF_XXX)) {
1072 		/* assert: anon share list is subset of overall server shares */
1073 		error = smb_browse(ctx, 1);
1074 		if (error) /* user cancel or other error? */
1075 			return (error);
1076 		/*
1077 		 * A share was selected, authenticate button was pressed,
1078 		 * or anon-authentication failed getting browse list.
1079 		 */
1080 	}
1081 	if ((ctx->ct_secblob == NULL) && (ctx->ct_flags & SMBCF_AUTHREQ ||
1082 	    (ssn->ioc_password[0] == '\0' &&
1083 	    !(ctx->ct_flags & SMBCF_NOPWD)))) {
1084 reauth:
1085 		/*
1086 		 * This function is implemented in both
1087 		 * ui-apple.c and ui-sun.c so let's try to
1088 		 * keep the same interface.  Not sure why
1089 		 * they didn't just pass ssn here.
1090 		 */
1091 		error = smb_get_authentication(
1092 		    ssn->ioc_workgroup, sizeof (ssn->ioc_workgroup) - 1,
1093 		    ssn->ioc_user, sizeof (ssn->ioc_user) - 1,
1094 		    ssn->ioc_password, sizeof (ssn->ioc_password) - 1,
1095 		    ssn->ioc_srvname, ctx);
1096 		if (error)
1097 			return (error);
1098 	}
1099 	/*
1100 	 * if we have a session it is either anonymous
1101 	 * or from a stale authentication.  re-negotiating
1102 	 * gets us ready for a fresh session
1103 	 */
1104 	if (ctx->ct_flags & SMBCF_SSNACTIVE || renego) {
1105 		renego = 0;
1106 		/* don't clobber workgroup name, pass null arg */
1107 		error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, NULL);
1108 		if (error)
1109 			return (error);
1110 	}
1111 	if (browseok && !sh->ioc_share[0]) {
1112 		ctx->ct_flags &= ~SMBCF_AUTHREQ;
1113 		error = smb_browse(ctx, 0);
1114 		if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) {
1115 			smb_error(dgettext(TEXT_DOMAIN,
1116 			    "smb_ctx_resolve: bad keychain entry"), 0);
1117 			ctx->ct_flags |= SMBCF_KCBAD;
1118 			renego = 1;
1119 			goto reauth;
1120 		}
1121 		if (error) /* auth, user cancel, or other error */
1122 			return (error);
1123 		/*
1124 		 * Re-authenticate button was pressed?
1125 		 */
1126 		if (ctx->ct_flags & SMBCF_AUTHREQ)
1127 			goto reauth;
1128 		if (!sh->ioc_share[0] && !(ctx->ct_flags & SMBCF_XXX)) {
1129 			smb_error(dgettext(TEXT_DOMAIN,
1130 			    "no share specified for %s@%s"),
1131 			    0, ssn->ioc_user, ssn->ioc_srvname);
1132 			return (EINVAL);
1133 		}
1134 	}
1135 	ctx->ct_flags |= SMBCF_RESOLVED;
1136 
1137 	if (smb_debug)
1138 		dump_ctx("after smb_ctx_resolve", ctx);
1139 
1140 	return (0);
1141 }
1142 
1143 int
1144 smb_open_driver()
1145 {
1146 	char buf[20];
1147 	int err, fd, i;
1148 	uint32_t version;
1149 
1150 	/*
1151 	 * First try to open as clone
1152 	 */
1153 	fd = open("/dev/"NSMB_NAME, O_RDWR);
1154 	if (fd >= 0)
1155 		goto opened;
1156 
1157 	err = errno; /* from open */
1158 #ifdef APPLE
1159 	/*
1160 	 * well, no clone capabilities available - we have to scan
1161 	 * all devices in order to get free one
1162 	 */
1163 	for (i = 0; i < 1024; i++) {
1164 		snprintf(buf, sizeof (buf), "/dev/%s%d", NSMB_NAME, i);
1165 		fd = open(buf, O_RDWR);
1166 		if (fd >= 0)
1167 			goto opened;
1168 		if (i && POWEROF2(i+1))
1169 			smb_error(dgettext(TEXT_DOMAIN,
1170 			    "%d failures to open smb device"), errno, i+1);
1171 	}
1172 	err = ENOENT;
1173 #endif
1174 	smb_error(dgettext(TEXT_DOMAIN,
1175 	    "failed to open %s"), err, "/dev/" NSMB_NAME);
1176 	return (-1);
1177 
1178 opened:
1179 	/*
1180 	 * Check the driver version (paranoia)
1181 	 * Do this BEFORE any other ioctl calls.
1182 	 */
1183 	if (ioctl(fd, SMBIOC_GETVERS, &version) < 0) {
1184 		err = errno;
1185 		smb_error(dgettext(TEXT_DOMAIN,
1186 		    "failed to get driver version"), err);
1187 		close(fd);
1188 		return (-1);
1189 	}
1190 	if (version != NSMB_VERSION) {
1191 		smb_error(dgettext(TEXT_DOMAIN,
1192 		    "incorrect driver version"), 0);
1193 		close(fd);
1194 		return (-1);
1195 	}
1196 
1197 	return (fd);
1198 }
1199 
1200 static int
1201 smb_ctx_gethandle(struct smb_ctx *ctx)
1202 {
1203 	int err, fd;
1204 
1205 	if (ctx->ct_fd != -1) {
1206 		rpc_cleanup_smbctx(ctx);
1207 		close(ctx->ct_fd);
1208 		ctx->ct_fd = -1;
1209 		ctx->ct_flags &= ~SMBCF_SSNACTIVE;
1210 	}
1211 
1212 	fd = smb_open_driver();
1213 	if (fd < 0)
1214 		return (ENODEV);
1215 
1216 	ctx->ct_fd = fd;
1217 	return (0);
1218 }
1219 
1220 int
1221 smb_ctx_ioctl(struct smb_ctx *ctx, int inum, struct smbioc_lookup *rqp)
1222 {
1223 	size_t	siz = DEF_SEC_TOKEN_LEN;
1224 	int	rc = 0;
1225 	struct sockaddr sap1, sap2;
1226 	int i;
1227 
1228 	if (rqp->ioc_ssn.ioc_outtok)
1229 		free(rqp->ioc_ssn.ioc_outtok);
1230 	rqp->ioc_ssn.ioc_outtoklen = siz;
1231 	rqp->ioc_ssn.ioc_outtok = malloc(siz+1);
1232 	if (rqp->ioc_ssn.ioc_outtok == NULL)
1233 		return (ENOMEM);
1234 	bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
1235 	/* Note: No longer put length in outtok[0] */
1236 	/* *((int *)rqp->ioc_ssn.ioc_outtok) = (int)siz; */
1237 
1238 	if (ioctl(ctx->ct_fd, inum, rqp) == -1) {
1239 		rc = errno;
1240 		goto out;
1241 	}
1242 	if (rqp->ioc_ssn.ioc_outtoklen <= siz)
1243 		goto out;
1244 
1245 	/*
1246 	 * Operation completed, but our output token wasn't large enough.
1247 	 * The re-call below only pulls the token from the kernel.
1248 	 */
1249 	siz = rqp->ioc_ssn.ioc_outtoklen;
1250 	free(rqp->ioc_ssn.ioc_outtok);
1251 	rqp->ioc_ssn.ioc_outtok = malloc(siz + 1);
1252 	if (rqp->ioc_ssn.ioc_outtok == NULL) {
1253 		rc = ENOMEM;
1254 		goto out;
1255 	}
1256 	bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
1257 	/* Note: No longer put length in outtok[0] */
1258 	/* *((int *)rqp->ioc_ssn.ioc_outtok) = siz; */
1259 	if (ioctl(ctx->ct_fd, inum, rqp) == -1)
1260 		rc = errno;
1261 out:
1262 	return (rc);
1263 }
1264 
1265 int
1266 smb_ctx_findvc(struct smb_ctx *ctx, int level, int flags)
1267 {
1268 	struct smbioc_lookup	rq;
1269 	int	error = 0;
1270 
1271 	if ((error = smb_ctx_gethandle(ctx)))
1272 		return (error);
1273 
1274 	bzero(&rq, sizeof (rq));
1275 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1276 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1277 
1278 	rq.ioc_flags = flags;
1279 	rq.ioc_level = level;
1280 
1281 	return (smb_ctx_ioctl(ctx, SMBIOC_FINDVC, &rq));
1282 }
1283 
1284 /*
1285  * adds a GSSAPI wrapper
1286  */
1287 char *
1288 smb_ctx_tkt2gtok(uchar_t *tkt, ulong_t tktlen,
1289     uchar_t **gtokp, ulong_t *gtoklenp)
1290 {
1291 	ulong_t		bloblen = tktlen;
1292 	ulong_t		len;
1293 	uchar_t		krbapreq[2] = "\x01\x00"; /* see RFC 1964 */
1294 	char 		*failure;
1295 	uchar_t 	*blob = NULL;		/* result */
1296 	uchar_t 	*b;
1297 
1298 	bloblen += sizeof (krbapreq);
1299 	bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen;
1300 	len = bloblen;
1301 	bloblen = ASNDerCalcTokenLength(bloblen, bloblen);
1302 	failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok malloc");
1303 	if (!(blob = malloc(bloblen)))
1304 		goto out;
1305 	b = blob;
1306 	b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len);
1307 	b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5);
1308 	memcpy(b, krbapreq, sizeof (krbapreq));
1309 	b += sizeof (krbapreq);
1310 	failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok insanity check");
1311 	if (b + tktlen != blob + bloblen)
1312 		goto out;
1313 	memcpy(b, tkt, tktlen);
1314 	*gtoklenp = bloblen;
1315 	*gtokp = blob;
1316 	failure = NULL;
1317 out:;
1318 	if (blob && failure)
1319 		free(blob);
1320 	return (failure);
1321 }
1322 
1323 
1324 /*
1325  * Initialization for Kerberos, pulled out of smb_ctx_principal2tkt.
1326  * This just gets our cached credentials, if we have any.
1327  * Based on the "klist" command.
1328  */
1329 char *
1330 smb_ctx_krb5init(struct smb_ctx *ctx)
1331 {
1332 	char *failure;
1333 	krb5_error_code	kerr;
1334 	krb5_context	kctx = NULL;
1335 	krb5_ccache 	kcc = NULL;
1336 	krb5_principal	kprin = NULL;
1337 
1338 	kerr = krb5_init_context(&kctx);
1339 	if (kerr) {
1340 		failure = "krb5_init_context";
1341 		goto out;
1342 	}
1343 	ctx->ct_krb5ctx = kctx;
1344 
1345 	/* non-default would instead use krb5_cc_resolve */
1346 	kerr = krb5_cc_default(kctx, &kcc);
1347 	if (kerr) {
1348 		failure = "krb5_cc_default";
1349 		goto out;
1350 	}
1351 	ctx->ct_krb5cc = kcc;
1352 
1353 	/*
1354 	 * Get the client principal (ticket),
1355 	 * or find out if we don't have one.
1356 	 */
1357 	kerr = krb5_cc_get_principal(kctx, kcc, &kprin);
1358 	if (kerr) {
1359 		failure = "krb5_cc_get_principal";
1360 		goto out;
1361 	}
1362 	ctx->ct_krb5cp = kprin;
1363 
1364 	if (smb_verbose) {
1365 		fprintf(stderr, gettext("Ticket cache: %s:%s\n"),
1366 		    krb5_cc_get_type(kctx, kcc),
1367 		    krb5_cc_get_name(kctx, kcc));
1368 	}
1369 	failure = NULL;
1370 
1371 out:
1372 	return (failure);
1373 }
1374 
1375 
1376 /*
1377  * See "Windows 2000 Kerberos Interoperability" paper by
1378  * Christopher Nebergall.  RC4 HMAC is the W2K default but
1379  * Samba support lagged (not due to Samba itself, but due to OS'
1380  * Kerberos implementations.)
1381  *
1382  * Only session enc type should matter, not ticket enc type,
1383  * per Sam Hartman on krbdev.
1384  *
1385  * Preauthentication failure topics in krb-protocol may help here...
1386  * try "John Brezak" and/or "Clifford Neuman" too.
1387  */
1388 static krb5_enctype kenctypes[] = {
1389 	ENCTYPE_ARCFOUR_HMAC,	/* defined in Tiger krb5.h */
1390 	ENCTYPE_DES_CBC_MD5,
1391 	ENCTYPE_DES_CBC_CRC,
1392 	ENCTYPE_NULL
1393 };
1394 
1395 /*
1396  * Obtain a kerberos ticket...
1397  * (if TLD != "gov" then pray first)
1398  */
1399 char *
1400 smb_ctx_principal2tkt(
1401 	struct smb_ctx *ctx, char *prin,
1402 	uchar_t **tktp, ulong_t *tktlenp)
1403 {
1404 	char 		*failure;
1405 	krb5_context	kctx = NULL;
1406 	krb5_error_code	kerr;
1407 	krb5_ccache	kcc = NULL;
1408 	krb5_principal	kprin = NULL, cprn = NULL;
1409 	krb5_creds	kcreds, *kcredsp = NULL;
1410 	krb5_auth_context	kauth = NULL;
1411 	krb5_data	kdata, kdata0;
1412 	uchar_t 		*tkt;
1413 
1414 	memset((char *)&kcreds, 0, sizeof (kcreds));
1415 	kdata0.length = 0;
1416 
1417 	/* These shoud have been done in smb_ctx_krb5init() */
1418 	if (ctx->ct_krb5ctx == NULL ||
1419 	    ctx->ct_krb5cc == NULL ||
1420 	    ctx->ct_krb5cp == NULL) {
1421 		failure = "smb_ctx_krb5init";
1422 		goto out;
1423 	}
1424 	kctx = ctx->ct_krb5ctx;
1425 	kcc  = ctx->ct_krb5cc;
1426 	cprn = ctx->ct_krb5cp;
1427 
1428 	failure = "krb5_set_default_tgs_enctypes";
1429 	if ((kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes)))
1430 		goto out;
1431 	/*
1432 	 * The following is an unrolling of krb5_mk_req.  Something like:
1433 	 * krb5_mk_req(kctx, &kauth, 0, service(prin), hostname(prin),
1434 	 *		&kdata0, kcc, &kdata);)
1435 	 * ...except we needed krb5_parse_name not krb5_sname_to_principal.
1436 	 */
1437 	failure = "krb5_parse_name";
1438 	if ((kerr = krb5_parse_name(kctx, prin, &kprin)))
1439 		goto out;
1440 	failure = "krb5_copy_principal(server)";
1441 	if ((kerr = krb5_copy_principal(kctx, kprin, &kcreds.server)))
1442 		goto out;
1443 	failure = "krb5_copy_principal(client)";
1444 	if ((kerr = krb5_copy_principal(kctx, cprn, &kcreds.client)))
1445 		goto out;
1446 	failure = "krb5_get_credentials";
1447 	if ((kerr = krb5_get_credentials(kctx, 0, kcc, &kcreds, &kcredsp)))
1448 		goto out;
1449 	failure = "krb5_mk_req_extended";
1450 	if ((kerr = krb5_mk_req_extended(kctx, &kauth, 0, &kdata0, kcredsp,
1451 	    &kdata)))
1452 		goto out;
1453 	failure = "malloc";
1454 	if (!(tkt = malloc(kdata.length))) {
1455 		krb5_free_data_contents(kctx, &kdata);
1456 		goto out;
1457 	}
1458 	*tktlenp = kdata.length;
1459 	memcpy(tkt, kdata.data, kdata.length);
1460 	krb5_free_data_contents(kctx, &kdata);
1461 	*tktp = tkt;
1462 	failure = NULL;
1463 out:;
1464 	if (kerr) {
1465 		if (!failure)
1466 			failure = "smb_ctx_principal2tkt";
1467 		/*
1468 		 * Avoid logging the typical "No credentials cache found"
1469 		 */
1470 		if (kerr != KRB5_FCC_NOFILE ||
1471 		    strcmp(failure, "krb5_cc_get_principal"))
1472 			com_err(__progname, kerr, failure);
1473 	}
1474 	if (kauth)
1475 		krb5_auth_con_free(kctx, kauth);
1476 	if (kcredsp)
1477 		krb5_free_creds(kctx, kcredsp);
1478 	if (kcreds.server || kcreds.client)
1479 		krb5_free_cred_contents(kctx, &kcreds);
1480 	if (kprin)
1481 		krb5_free_principal(kctx, kprin);
1482 
1483 	/* Free kctx in smb_ctx_done */
1484 
1485 	return (failure);
1486 }
1487 
1488 char *
1489 smb_ctx_principal2blob(
1490 	struct smb_ctx *ctx,
1491 	smbioc_ossn_t *ssn,
1492 	char *prin)
1493 {
1494 	int		rc = 0;
1495 	char 		*failure;
1496 	uchar_t 	*tkt = NULL;
1497 	ulong_t		tktlen;
1498 	uchar_t 	*gtok = NULL;		/* gssapi token */
1499 	ulong_t		gtoklen;		/* gssapi token length */
1500 	SPNEGO_TOKEN_HANDLE  stok = NULL;	/* spnego token */
1501 	void 	*blob = NULL;		/* result */
1502 	ulong_t		bloblen;		/* result length */
1503 
1504 	if ((failure = smb_ctx_principal2tkt(ctx, prin, &tkt, &tktlen)))
1505 		goto out;
1506 	if ((failure = smb_ctx_tkt2gtok(tkt, tktlen, &gtok, &gtoklen)))
1507 		goto out;
1508 	/*
1509 	 * RFC says to send NegTokenTarg now.  So does MS docs.  But
1510 	 * win2k gives ERRbaduid if we do...  we must send
1511 	 * another NegTokenInit now!
1512 	 */
1513 	failure = "spnegoCreateNegTokenInit";
1514 	if ((rc = spnegoCreateNegTokenInit(spnego_mech_oid_Kerberos_V5_Legacy,
1515 	    0, gtok, gtoklen, NULL, 0, &stok)))
1516 		goto out;
1517 	failure = "spnegoTokenGetBinary(NULL)";
1518 	rc = spnegoTokenGetBinary(stok, NULL, &bloblen);
1519 	if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
1520 		goto out;
1521 	failure = "malloc";
1522 	if (!(blob = malloc((size_t)bloblen)))
1523 		goto out;
1524 	/* No longer store length at start of blob. */
1525 	/* *blob = bloblen; */
1526 	failure = "spnegoTokenGetBinary";
1527 	if ((rc = spnegoTokenGetBinary(stok, blob, &bloblen)))
1528 		goto out;
1529 	ssn->ioc_intoklen = bloblen;
1530 	ssn->ioc_intok = blob;
1531 	failure = NULL;
1532 out:;
1533 	if (rc) {
1534 		/* XXX better is to embed rc in failure */
1535 		smb_error(dgettext(TEXT_DOMAIN,
1536 		    "spnego principal2blob error %d"), 0, -rc);
1537 		if (!failure)
1538 			failure = "spnego";
1539 	}
1540 	if (blob && failure)
1541 		free(blob);
1542 	if (stok)
1543 		spnegoFreeData(stok);
1544 	if (gtok)
1545 		free(gtok);
1546 	if (tkt)
1547 		free(tkt);
1548 	return (failure);
1549 }
1550 
1551 
1552 #if 0
1553 void
1554 prblob(uchar_t *b, size_t len)
1555 {
1556 	while (len--)
1557 		fprintf(stderr, "%02x", *b++);
1558 	fprintf(stderr, "\n");
1559 }
1560 #endif
1561 
1562 
1563 /*
1564  * We navigate the SPNEGO & ASN1 encoding to find a kerberos principal
1565  * Note: driver no longer puts length at start of blob.
1566  */
1567 char *
1568 smb_ctx_blob2principal(
1569 	struct smb_ctx *ctx,
1570 	smbioc_ossn_t *ssn,
1571 	char **prinp)
1572 {
1573 	uchar_t		*blob = ssn->ioc_outtok;
1574 	size_t		len = ssn->ioc_outtoklen;
1575 	int		rc = 0;
1576 	SPNEGO_TOKEN_HANDLE	stok = NULL;
1577 	int		indx = 0;
1578 	char 		*failure;
1579 	uchar_t		flags = 0;
1580 	unsigned long	plen = 0;
1581 	uchar_t 	*prin;
1582 
1583 #if 0
1584 	fprintf(stderr, "blob from negotiate:\n");
1585 	prblob(blob, len);
1586 #endif
1587 
1588 	/* Skip the GUID */
1589 	assert(len >= SMB_GUIDLEN);
1590 	blob += SMB_GUIDLEN;
1591 	len  -= SMB_GUIDLEN;
1592 
1593 	failure = "spnegoInitFromBinary";
1594 	if ((rc = spnegoInitFromBinary(blob, len, &stok)))
1595 		goto out;
1596 	/*
1597 	 * Needn't use new Kerberos OID - the Legacy one is fine.
1598 	 */
1599 	failure = "spnegoIsMechTypeAvailable";
1600 	if (spnegoIsMechTypeAvailable(stok, spnego_mech_oid_Kerberos_V5_Legacy,
1601 	    &indx))
1602 		goto out;
1603 	/*
1604 	 * Ignoring optional context flags for now.  May want to pass
1605 	 * them to krb5 layer.  XXX
1606 	 */
1607 	if (!spnegoGetContextFlags(stok, &flags))
1608 		fprintf(stderr, dgettext(TEXT_DOMAIN,
1609 		    "spnego context flags 0x%x\n"), flags);
1610 	failure = "spnegoGetMechListMIC(NULL)";
1611 	rc = spnegoGetMechListMIC(stok, NULL, &plen);
1612 	if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
1613 		goto out;
1614 	failure = "malloc";
1615 	if (!(prin = malloc(plen + 1)))
1616 		goto out;
1617 	failure = "spnegoGetMechListMIC";
1618 	if ((rc = spnegoGetMechListMIC(stok, prin, &plen))) {
1619 		free(prin);
1620 		goto out;
1621 	}
1622 	prin[plen] = '\0';
1623 	*prinp = (char *)prin;
1624 	failure = NULL;
1625 out:;
1626 	if (stok)
1627 		spnegoFreeData(stok);
1628 	if (rc) {
1629 		/* XXX better is to embed rc in failure */
1630 		smb_error(dgettext(TEXT_DOMAIN,
1631 		    "spnego blob2principal error %d"), 0, -rc);
1632 		if (!failure)
1633 			failure = "spnego";
1634 	}
1635 	return (failure);
1636 }
1637 
1638 
1639 int
1640 smb_ctx_negotiate(struct smb_ctx *ctx, int level, int flags, char *workgroup)
1641 {
1642 	struct smbioc_lookup	rq;
1643 	int	error = 0;
1644 	char 	*failure = NULL;
1645 	char	*principal = NULL;
1646 	char c;
1647 	int i;
1648 	ssize_t *outtoklen;
1649 	uchar_t *blob;
1650 
1651 	/*
1652 	 * We leave ct_secblob set iff extended security
1653 	 * negotiation succeeds.
1654 	 */
1655 	if (ctx->ct_secblob) {
1656 		free(ctx->ct_secblob);
1657 		ctx->ct_secblob = NULL;
1658 	}
1659 #ifdef XXX
1660 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
1661 		smb_error(dgettext(TEXT_DOMAIN,
1662 		    "smb_ctx_lookup() data is not resolved"), 0);
1663 		return (EINVAL);
1664 	}
1665 #endif
1666 	if ((error = smb_ctx_gethandle(ctx)))
1667 		return (error);
1668 
1669 	bzero(&rq, sizeof (rq));
1670 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1671 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1672 
1673 	/*
1674 	 * Find out if we have a Kerberos ticket,
1675 	 * and only offer SPNEGO if we have one.
1676 	 */
1677 	failure = smb_ctx_krb5init(ctx);
1678 	if (failure) {
1679 		if (smb_verbose)
1680 			smb_error(failure, 0);
1681 		goto out;
1682 	}
1683 
1684 	rq.ioc_flags = flags;
1685 	rq.ioc_level = level;
1686 	rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
1687 	error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
1688 	if (error) {
1689 		failure = dgettext(TEXT_DOMAIN, "negotiate failed");
1690 		smb_error(failure, error);
1691 		if (error == ETIMEDOUT)
1692 			return (error);
1693 		goto out;
1694 	}
1695 	/*
1696 	 * If the server capabilities did not include
1697 	 * SMB_CAP_EXT_SECURITY then the driver clears
1698 	 * the flag SMBVOPT_EXT_SEC for us.
1699 	 * XXX: should add the capabilities to ioc_ssn
1700 	 * XXX: see comment in driver - smb_usr.c
1701 	 */
1702 	failure = dgettext(TEXT_DOMAIN, "SPNEGO unsupported");
1703 	if ((rq.ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC) == 0) {
1704 		if (smb_verbose)
1705 			smb_error(failure, 0);
1706 		/*
1707 		 * Do regular (old style) NTLM or NTLMv2
1708 		 * Nothing more to do here in negotiate.
1709 		 */
1710 		return (0);
1711 	}
1712 
1713 	/*
1714 	 * Capabilities DO include SMB_CAP_EXT_SECURITY,
1715 	 * so this should be an SPNEGO security blob.
1716 	 * Parse the ASN.1/DER, prepare response(s).
1717 	 * XXX: Handle STATUS_MORE_PROCESSING_REQUIRED?
1718 	 * XXX: Requires additional session setup calls.
1719 	 */
1720 	if (rq.ioc_ssn.ioc_outtoklen <= SMB_GUIDLEN)
1721 		goto out;
1722 	/* some servers send padding junk */
1723 	blob = rq.ioc_ssn.ioc_outtok;
1724 	if (blob[0] == 0)
1725 		goto out;
1726 
1727 	failure = smb_ctx_blob2principal(
1728 	    ctx, &rq.ioc_ssn, &principal);
1729 	if (failure)
1730 		goto out;
1731 	failure = smb_ctx_principal2blob(
1732 	    ctx, &rq.ioc_ssn, principal);
1733 	if (failure)
1734 		goto out;
1735 
1736 	/* Success! Save the blob to send next. */
1737 	ctx->ct_secblob = rq.ioc_ssn.ioc_intok;
1738 	ctx->ct_secbloblen = rq.ioc_ssn.ioc_intoklen;
1739 	rq.ioc_ssn.ioc_intok = NULL;
1740 
1741 out:
1742 	if (principal)
1743 		free(principal);
1744 	if (rq.ioc_ssn.ioc_intok)
1745 		free(rq.ioc_ssn.ioc_intok);
1746 	if (rq.ioc_ssn.ioc_outtok)
1747 		free(rq.ioc_ssn.ioc_outtok);
1748 	if (!failure)
1749 		return (0);		/* Success! */
1750 
1751 	/*
1752 	 * Negotiate failed with "extended security".
1753 	 *
1754 	 * XXX: If we are doing SPNEGO correctly,
1755 	 * we should never get here unless the user
1756 	 * supplied invalid authentication data,
1757 	 * or we saw some kind of protocol error.
1758 	 *
1759 	 * XXX: The error message below should be
1760 	 * XXX: unconditional (remove "if verbose")
1761 	 * XXX: but not until we have "NTLMSSP"
1762 	 * Avoid spew for anticipated failure modes
1763 	 * but enable this with the verbose flag
1764 	 */
1765 	if (smb_verbose) {
1766 		smb_error(dgettext(TEXT_DOMAIN,
1767 		    "%s (extended security negotiate)"), error, failure);
1768 	}
1769 
1770 	/*
1771 	 * XXX: Try again using NTLM (or NTLMv2)
1772 	 * XXX: Normal clients don't do this.
1773 	 * XXX: Should just return an error, but
1774 	 * keep the fall-back to NTLM for now.
1775 	 *
1776 	 * Start over with a new connection.
1777 	 */
1778 	if ((error = smb_ctx_gethandle(ctx)))
1779 		return (error);
1780 	bzero(&rq, sizeof (rq));
1781 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1782 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1783 	rq.ioc_flags = flags;
1784 	rq.ioc_level = level;
1785 	/* Note: NO SMBVOPT_EXT_SEC */
1786 	error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
1787 	if (error) {
1788 		failure = dgettext(TEXT_DOMAIN, "negotiate failed");
1789 		smb_error(failure, error);
1790 		rpc_cleanup_smbctx(ctx);
1791 		close(ctx->ct_fd);
1792 		ctx->ct_fd = -1;
1793 		return (error);
1794 	}
1795 
1796 	/*
1797 	 * Used to copy the workgroup out of the SMB_NEGOTIATE response
1798 	 * here, to default our domain name to be the same as the server.
1799 	 * Not a good idea: Unnecessary at best, and sometimes wrong, i.e.
1800 	 * when our account is in a trusted domain.
1801 	 */
1802 
1803 	return (error);
1804 }
1805 
1806 
1807 int
1808 smb_ctx_tdis(struct smb_ctx *ctx)
1809 {
1810 	struct smbioc_lookup rq; /* XXX may be used, someday */
1811 	int error = 0;
1812 
1813 	if (ctx->ct_fd < 0) {
1814 		smb_error(dgettext(TEXT_DOMAIN,
1815 		    "tree disconnect without handle?!"), 0);
1816 		return (EINVAL);
1817 	}
1818 	if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
1819 		smb_error(dgettext(TEXT_DOMAIN,
1820 		    "tree disconnect without session?!"), 0);
1821 		return (EINVAL);
1822 	}
1823 	bzero(&rq, sizeof (rq));
1824 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1825 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1826 	if (ioctl(ctx->ct_fd, SMBIOC_TDIS, &rq) == -1) {
1827 		error = errno;
1828 		smb_error(dgettext(TEXT_DOMAIN,
1829 		    "tree disconnect failed"), error);
1830 	}
1831 	return (error);
1832 }
1833 
1834 
1835 int
1836 smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
1837 {
1838 	struct smbioc_lookup rq;
1839 	int error = 0;
1840 	char 	*failure = NULL;
1841 
1842 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
1843 		smb_error(dgettext(TEXT_DOMAIN,
1844 		    "smb_ctx_lookup() data is not resolved"), 0);
1845 		return (EINVAL);
1846 	}
1847 	if (ctx->ct_fd < 0) {
1848 		smb_error(dgettext(TEXT_DOMAIN,
1849 		    "handle from smb_ctx_nego() gone?!"), 0);
1850 		return (EINVAL);
1851 	}
1852 	if (!(flags & SMBLK_CREATE))
1853 		return (0);
1854 	bzero(&rq, sizeof (rq));
1855 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1856 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1857 	rq.ioc_flags = flags;
1858 	rq.ioc_level = level;
1859 
1860 	/*
1861 	 * Iff we have a security blob, we're using
1862 	 * extended security...
1863 	 */
1864 	if (ctx->ct_secblob) {
1865 		rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
1866 		if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
1867 			rq.ioc_ssn.ioc_intok = ctx->ct_secblob;
1868 			rq.ioc_ssn.ioc_intoklen = ctx->ct_secbloblen;
1869 			error = smb_ctx_ioctl(ctx, SMBIOC_SSNSETUP, &rq);
1870 		}
1871 		rq.ioc_ssn.ioc_intok = NULL;
1872 		if (error) {
1873 			failure = dgettext(TEXT_DOMAIN,
1874 			    "session setup failed");
1875 		} else {
1876 			ctx->ct_flags |= SMBCF_SSNACTIVE;
1877 			if ((error = smb_ctx_ioctl(ctx, SMBIOC_TCON, &rq)))
1878 				failure = dgettext(TEXT_DOMAIN,
1879 				    "tree connect failed");
1880 		}
1881 		if (rq.ioc_ssn.ioc_intok)
1882 			free(rq.ioc_ssn.ioc_intok);
1883 		if (rq.ioc_ssn.ioc_outtok)
1884 			free(rq.ioc_ssn.ioc_outtok);
1885 		if (!failure)
1886 			return (0);
1887 		smb_error(dgettext(TEXT_DOMAIN,
1888 		    "%s (extended security lookup2)"), error, failure);
1889 		/* unwise to failback to NTLM now */
1890 		return (error);
1891 	}
1892 
1893 	/*
1894 	 * Otherwise we're doing plain old NTLM
1895 	 */
1896 	if ((ctx->ct_flags & SMBCF_SSNACTIVE) == 0) {
1897 		/*
1898 		 * This is the magic that tells the driver to
1899 		 * copy the password from the keychain, and
1900 		 * whether to use the system name or the
1901 		 * account domain to lookup the keychain.
1902 		 */
1903 		if (ctx->ct_flags & SMBCF_KCFOUND)
1904 			rq.ioc_ssn.ioc_opt |= SMBVOPT_USE_KEYCHAIN;
1905 		if (ctx->ct_flags & SMBCF_KCDOMAIN)
1906 			rq.ioc_ssn.ioc_opt |= SMBVOPT_KC_DOMAIN;
1907 		if (ioctl(ctx->ct_fd, SMBIOC_SSNSETUP, &rq) < 0) {
1908 			error = errno;
1909 			failure = dgettext(TEXT_DOMAIN, "session setup");
1910 			goto out;
1911 		}
1912 		ctx->ct_flags |= SMBCF_SSNACTIVE;
1913 	}
1914 	if (ioctl(ctx->ct_fd, SMBIOC_TCON, &rq) == -1) {
1915 		error = errno;
1916 		failure = dgettext(TEXT_DOMAIN, "tree connect");
1917 	}
1918 
1919 out:
1920 	if (failure) {
1921 		error = errno;
1922 		smb_error(dgettext(TEXT_DOMAIN,
1923 		    "%s phase failed"), error, failure);
1924 	}
1925 	return (error);
1926 }
1927 
1928 /*
1929  * Return the hflags2 word for an smb_ctx.
1930  */
1931 int
1932 smb_ctx_flags2(struct smb_ctx *ctx)
1933 {
1934 	uint16_t flags2;
1935 
1936 	if (ioctl(ctx->ct_fd, SMBIOC_FLAGS2, &flags2) == -1) {
1937 		smb_error(dgettext(TEXT_DOMAIN,
1938 		    "can't get flags2 for a session"), errno);
1939 		return (-1);
1940 	}
1941 	return (flags2);
1942 }
1943 
1944 /*
1945  * level values:
1946  * 0 - default
1947  * 1 - server
1948  * 2 - server:user
1949  * 3 - server:user:share
1950  */
1951 static int
1952 smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
1953 {
1954 	char *p;
1955 	int error;
1956 
1957 #ifdef NOT_DEFINED
1958 	if (level > 0) {
1959 		rc_getstringptr(smb_rc, sname, "charsets", &p);
1960 		if (p) {
1961 			error = smb_ctx_setcharset(ctx, p);
1962 			if (error)
1963 				smb_error(dgettext(TEXT_DOMAIN,
1964 	"charset specification in the section '%s' ignored"),
1965 				    error, sname);
1966 		}
1967 	}
1968 #endif
1969 
1970 	if (level <= 1) {
1971 		/* Section is: [default] or [server] */
1972 
1973 		rc_getint(smb_rc, sname, "timeout",
1974 		    &ctx->ct_ssn.ioc_timeout);
1975 
1976 #ifdef NOT_DEFINED
1977 		rc_getint(smb_rc, sname, "retry_count",
1978 		    &ctx->ct_ssn.ioc_retrycount);
1979 		rc_getstringptr(smb_rc, sname, "use_negprot_domain", &p);
1980 		if (p && strcmp(p, "NO") == 0)
1981 			ctx->ct_flags |= SMBCF_NONEGDOM;
1982 #endif
1983 
1984 		rc_getstringptr(smb_rc, sname, "minauth", &p);
1985 		if (p) {
1986 			/*
1987 			 * "minauth" was set in this section; override
1988 			 * the current minimum authentication setting.
1989 			 */
1990 			ctx->ct_ssn.ioc_opt &= ~SMBVOPT_MINAUTH;
1991 			if (strcmp(p, "kerberos") == 0) {
1992 				/*
1993 				 * Don't fall back to NTLMv2, NTLMv1, or
1994 				 * a clear text password.
1995 				 */
1996 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_KERBEROS;
1997 			} else if (strcmp(p, "ntlmv2") == 0) {
1998 				/*
1999 				 * Don't fall back to NTLMv1 or a clear
2000 				 * text password.
2001 				 */
2002 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLMV2;
2003 			} else if (strcmp(p, "ntlm") == 0) {
2004 				/*
2005 				 * Don't send the LM response over the wire.
2006 				 */
2007 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLM;
2008 			} else if (strcmp(p, "lm") == 0) {
2009 				/*
2010 				 * Fail if the server doesn't do encrypted
2011 				 * passwords.
2012 				 */
2013 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_LM;
2014 			} else if (strcmp(p, "none") == 0) {
2015 				/*
2016 				 * Anything goes.
2017 				 * (The following statement should be
2018 				 * optimized away.)
2019 				 */
2020 				/* LINTED */
2021 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NONE;
2022 			} else {
2023 				/*
2024 				 * Unknown minimum authentication level.
2025 				 */
2026 				smb_error(dgettext(TEXT_DOMAIN,
2027 "invalid minimum authentication level \"%s\" specified in the section %s"),
2028 				    0, p, sname);
2029 				return (EINVAL);
2030 			}
2031 		}
2032 
2033 		rc_getstringptr(smb_rc, sname, "signing", &p);
2034 		if (p) {
2035 			/*
2036 			 * "signing" was set in this section; override
2037 			 * the current signing settings.
2038 			 */
2039 			ctx->ct_ssn.ioc_opt &= ~SMBVOPT_SIGNING_MASK;
2040 			if (strcmp(p, "disabled") == 0) {
2041 				/* leave flags zero (expr for lint) */
2042 				(void) ctx->ct_ssn.ioc_opt;
2043 			} else if (strcmp(p, "enabled") == 0) {
2044 				ctx->ct_ssn.ioc_opt |=
2045 				    SMBVOPT_SIGNING_ENABLED;
2046 			} else if (strcmp(p, "required") == 0) {
2047 				ctx->ct_ssn.ioc_opt |=
2048 				    SMBVOPT_SIGNING_ENABLED |
2049 				    SMBVOPT_SIGNING_REQUIRED;
2050 			} else {
2051 				/*
2052 				 * Unknown "signing" value.
2053 				 */
2054 				smb_error(dgettext(TEXT_DOMAIN,
2055 "invalid signing policy \"%s\" specified in the section %s"),
2056 				    0, p, sname);
2057 				return (EINVAL);
2058 			}
2059 		}
2060 
2061 		/*
2062 		 * Domain name.  Allow both keywords:
2063 		 * "workgroup", "domain"
2064 		 *
2065 		 * Note: these are NOT marked "from CMD".
2066 		 * See long comment at smb_ctx_init()
2067 		 */
2068 		rc_getstringptr(smb_rc, sname, "workgroup", &p);
2069 		if (p) {
2070 			nls_str_upper(p, p);
2071 			error = smb_ctx_setworkgroup(ctx, p, 0);
2072 			if (error)
2073 				smb_error(dgettext(TEXT_DOMAIN,
2074 				    "workgroup specification in the "
2075 				    "section '%s' ignored"), error, sname);
2076 		}
2077 		rc_getstringptr(smb_rc, sname, "domain", &p);
2078 		if (p) {
2079 			nls_str_upper(p, p);
2080 			error = smb_ctx_setworkgroup(ctx, p, 0);
2081 			if (error)
2082 				smb_error(dgettext(TEXT_DOMAIN,
2083 				    "domain specification in the "
2084 				    "section '%s' ignored"), error, sname);
2085 		}
2086 
2087 		rc_getstringptr(smb_rc, sname, "user", &p);
2088 		if (p) {
2089 			error = smb_ctx_setuser(ctx, p, 0);
2090 			if (error)
2091 				smb_error(dgettext(TEXT_DOMAIN,
2092 				    "user specification in the "
2093 				    "section '%s' ignored"), error, sname);
2094 		}
2095 	}
2096 
2097 	if (level == 1) {
2098 		/* Section is: [server] */
2099 		rc_getstringptr(smb_rc, sname, "addr", &p);
2100 		if (p) {
2101 			error = smb_ctx_setsrvaddr(ctx, p);
2102 			if (error) {
2103 				smb_error(dgettext(TEXT_DOMAIN,
2104 				    "invalid address specified in section %s"),
2105 				    0, sname);
2106 				return (error);
2107 			}
2108 		}
2109 	}
2110 
2111 	rc_getstringptr(smb_rc, sname, "password", &p);
2112 	if (p) {
2113 		error = smb_ctx_setpassword(ctx, p, 0);
2114 		if (error)
2115 			smb_error(dgettext(TEXT_DOMAIN,
2116 	    "password specification in the section '%s' ignored"),
2117 			    error, sname);
2118 	}
2119 
2120 	return (0);
2121 }
2122 
2123 /*
2124  * read rc file as follows:
2125  * 0: read [default] section
2126  * 1: override with [server] section
2127  * 2: override with [server:user] section
2128  * 3: override with [server:user:share] section
2129  * Since absence of rcfile is not fatal, silently ignore this fact.
2130  * smb_rc file should be closed by caller.
2131  */
2132 int
2133 smb_ctx_readrc(struct smb_ctx *ctx)
2134 {
2135 	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN +
2136 	    SMB_MAXSHARENAMELEN + 4];
2137 
2138 	if (smb_open_rcfile(ctx) != 0)
2139 		goto done;
2140 
2141 	/*
2142 	 * default parameters (level=0)
2143 	 */
2144 	smb_ctx_readrcsection(ctx, "default", 0);
2145 	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
2146 
2147 	/*
2148 	 * If we don't have a server name, we can't read any of the
2149 	 * [server...] sections.
2150 	 */
2151 	if (ctx->ct_ssn.ioc_srvname[0] == 0)
2152 		goto done;
2153 
2154 	/*
2155 	 * SERVER parameters.
2156 	 */
2157 	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
2158 
2159 	/*
2160 	 * If we don't have a user name, we can't read any of the
2161 	 * [server:user...] sections.
2162 	 */
2163 	if (ctx->ct_ssn.ioc_user[0] == 0)
2164 		goto done;
2165 
2166 	/*
2167 	 * SERVER:USER parameters
2168 	 */
2169 	snprintf(sname, sizeof (sname), "%s:%s",
2170 	    ctx->ct_ssn.ioc_srvname,
2171 	    ctx->ct_ssn.ioc_user);
2172 	smb_ctx_readrcsection(ctx, sname, 2);
2173 
2174 	/*
2175 	 * If we don't have a share name, we can't read any of the
2176 	 * [server:user:share] sections.
2177 	 */
2178 	if (ctx->ct_sh.ioc_share[0] != 0) {
2179 		/*
2180 		 * SERVER:USER:SHARE parameters
2181 		 */
2182 		snprintf(sname, sizeof (sname), "%s:%s:%s",
2183 		    ctx->ct_ssn.ioc_srvname,
2184 		    ctx->ct_ssn.ioc_user,
2185 		    ctx->ct_sh.ioc_share);
2186 		smb_ctx_readrcsection(ctx, sname, 3);
2187 	}
2188 
2189 done:
2190 	if (smb_debug)
2191 		dump_ctx("after smb_ctx_readrc", ctx);
2192 
2193 	return (0);
2194 }
2195