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