xref: /titanic_44/usr/src/lib/libsmbfs/smb/ctx.c (revision 7b209c2cc5ea45251aba06dcc6181d3f23da807a)
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_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE,
1053 	    ssn->ioc_workgroup);
1054 	if (error)
1055 		return (error);
1056 	ctx->ct_flags &= ~SMBCF_AUTHREQ;
1057 	if (!ctx->ct_secblob && browseok && !sh->ioc_share[0] &&
1058 	    !(ctx->ct_flags & SMBCF_XXX)) {
1059 		/* assert: anon share list is subset of overall server shares */
1060 		error = smb_browse(ctx, 1);
1061 		if (error) /* user cancel or other error? */
1062 			return (error);
1063 		/*
1064 		 * A share was selected, authenticate button was pressed,
1065 		 * or anon-authentication failed getting browse list.
1066 		 */
1067 	}
1068 	if ((ctx->ct_secblob == NULL) && (ctx->ct_flags & SMBCF_AUTHREQ ||
1069 	    (ssn->ioc_password[0] == '\0' &&
1070 	    !(ctx->ct_flags & SMBCF_NOPWD)))) {
1071 reauth:
1072 		/*
1073 		 * This function is implemented in both
1074 		 * ui-apple.c and ui-sun.c so let's try to
1075 		 * keep the same interface.  Not sure why
1076 		 * they didn't just pass ssn here.
1077 		 */
1078 		error = smb_get_authentication(
1079 		    ssn->ioc_workgroup, sizeof (ssn->ioc_workgroup) - 1,
1080 		    ssn->ioc_user, sizeof (ssn->ioc_user) - 1,
1081 		    ssn->ioc_password, sizeof (ssn->ioc_password) - 1,
1082 		    ssn->ioc_srvname, ctx);
1083 		if (error)
1084 			return (error);
1085 	}
1086 	/*
1087 	 * if we have a session it is either anonymous
1088 	 * or from a stale authentication.  re-negotiating
1089 	 * gets us ready for a fresh session
1090 	 */
1091 	if (ctx->ct_flags & SMBCF_SSNACTIVE || renego) {
1092 		renego = 0;
1093 		/* don't clobber workgroup name, pass null arg */
1094 		error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, NULL);
1095 		if (error)
1096 			return (error);
1097 	}
1098 	if (browseok && !sh->ioc_share[0]) {
1099 		ctx->ct_flags &= ~SMBCF_AUTHREQ;
1100 		error = smb_browse(ctx, 0);
1101 		if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) {
1102 			smb_error(dgettext(TEXT_DOMAIN,
1103 			    "smb_ctx_resolve: bad keychain entry"), 0);
1104 			ctx->ct_flags |= SMBCF_KCBAD;
1105 			renego = 1;
1106 			goto reauth;
1107 		}
1108 		if (error) /* auth, user cancel, or other error */
1109 			return (error);
1110 		/*
1111 		 * Re-authenticate button was pressed?
1112 		 */
1113 		if (ctx->ct_flags & SMBCF_AUTHREQ)
1114 			goto reauth;
1115 		if (!sh->ioc_share[0] && !(ctx->ct_flags & SMBCF_XXX)) {
1116 			smb_error(dgettext(TEXT_DOMAIN,
1117 			    "no share specified for %s@%s"),
1118 			    0, ssn->ioc_user, ssn->ioc_srvname);
1119 			return (EINVAL);
1120 		}
1121 	}
1122 	ctx->ct_flags |= SMBCF_RESOLVED;
1123 
1124 	if (smb_debug)
1125 		dump_ctx("after smb_ctx_resolve", ctx);
1126 
1127 	return (0);
1128 }
1129 
1130 int
1131 smb_open_driver()
1132 {
1133 	char buf[20];
1134 	int err, fd, i;
1135 	uint32_t version;
1136 
1137 	/*
1138 	 * First try to open as clone
1139 	 */
1140 	fd = open("/dev/"NSMB_NAME, O_RDWR);
1141 	if (fd >= 0)
1142 		goto opened;
1143 
1144 	err = errno; /* from open */
1145 #ifdef APPLE
1146 	/*
1147 	 * well, no clone capabilities available - we have to scan
1148 	 * all devices in order to get free one
1149 	 */
1150 	for (i = 0; i < 1024; i++) {
1151 		snprintf(buf, sizeof (buf), "/dev/%s%d", NSMB_NAME, i);
1152 		fd = open(buf, O_RDWR);
1153 		if (fd >= 0)
1154 			goto opened;
1155 		if (i && POWEROF2(i+1))
1156 			smb_error(dgettext(TEXT_DOMAIN,
1157 			    "%d failures to open smb device"), errno, i+1);
1158 	}
1159 	err = ENOENT;
1160 #endif
1161 	smb_error(dgettext(TEXT_DOMAIN,
1162 	    "failed to open %s"), err, "/dev/" NSMB_NAME);
1163 	return (-1);
1164 
1165 opened:
1166 	/*
1167 	 * Check the driver version (paranoia)
1168 	 * Do this BEFORE any other ioctl calls.
1169 	 */
1170 	if (ioctl(fd, SMBIOC_GETVERS, &version) < 0) {
1171 		err = errno;
1172 		smb_error(dgettext(TEXT_DOMAIN,
1173 		    "failed to get driver version"), err);
1174 		close(fd);
1175 		return (-1);
1176 	}
1177 	if (version != NSMB_VERSION) {
1178 		smb_error(dgettext(TEXT_DOMAIN,
1179 		    "incorrect driver version"), 0);
1180 		close(fd);
1181 		return (-1);
1182 	}
1183 
1184 	return (fd);
1185 }
1186 
1187 static int
1188 smb_ctx_gethandle(struct smb_ctx *ctx)
1189 {
1190 	int err, fd;
1191 
1192 	if (ctx->ct_fd != -1) {
1193 		rpc_cleanup_smbctx(ctx);
1194 		close(ctx->ct_fd);
1195 		ctx->ct_fd = -1;
1196 		ctx->ct_flags &= ~SMBCF_SSNACTIVE;
1197 	}
1198 
1199 	fd = smb_open_driver();
1200 	if (fd < 0)
1201 		return (ENODEV);
1202 
1203 	ctx->ct_fd = fd;
1204 	return (0);
1205 }
1206 
1207 int
1208 smb_ctx_ioctl(struct smb_ctx *ctx, int inum, struct smbioc_lookup *rqp)
1209 {
1210 	size_t	siz = DEF_SEC_TOKEN_LEN;
1211 	int	rc = 0;
1212 	struct sockaddr sap1, sap2;
1213 	int i;
1214 
1215 	if (rqp->ioc_ssn.ioc_outtok)
1216 		free(rqp->ioc_ssn.ioc_outtok);
1217 	rqp->ioc_ssn.ioc_outtoklen = siz;
1218 	rqp->ioc_ssn.ioc_outtok = malloc(siz+1);
1219 	if (rqp->ioc_ssn.ioc_outtok == NULL)
1220 		return (ENOMEM);
1221 	bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
1222 	/* Note: No longer put length in outtok[0] */
1223 	/* *((int *)rqp->ioc_ssn.ioc_outtok) = (int)siz; */
1224 
1225 	seteuid(eff_uid); /* restore setuid root briefly */
1226 	if (ioctl(ctx->ct_fd, inum, rqp) == -1) {
1227 		rc = errno;
1228 		goto out;
1229 	}
1230 	if (rqp->ioc_ssn.ioc_outtoklen <= siz)
1231 		goto out;
1232 
1233 	/*
1234 	 * Operation completed, but our output token wasn't large enough.
1235 	 * The re-call below only pulls the token from the kernel.
1236 	 */
1237 	siz = rqp->ioc_ssn.ioc_outtoklen;
1238 	free(rqp->ioc_ssn.ioc_outtok);
1239 	rqp->ioc_ssn.ioc_outtok = malloc(siz + 1);
1240 	if (rqp->ioc_ssn.ioc_outtok == NULL) {
1241 		rc = ENOMEM;
1242 		goto out;
1243 	}
1244 	bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
1245 	/* Note: No longer put length in outtok[0] */
1246 	/* *((int *)rqp->ioc_ssn.ioc_outtok) = siz; */
1247 	if (ioctl(ctx->ct_fd, inum, rqp) == -1)
1248 		rc = errno;
1249 out:
1250 	seteuid(real_uid); /* and back to real user */
1251 	return (rc);
1252 }
1253 
1254 
1255 /*
1256  * adds a GSSAPI wrapper
1257  */
1258 char *
1259 smb_ctx_tkt2gtok(uchar_t *tkt, ulong_t tktlen,
1260     uchar_t **gtokp, ulong_t *gtoklenp)
1261 {
1262 	ulong_t		bloblen = tktlen;
1263 	ulong_t		len;
1264 	uchar_t		krbapreq[2] = "\x01\x00"; /* see RFC 1964 */
1265 	char 		*failure;
1266 	uchar_t 	*blob = NULL;		/* result */
1267 	uchar_t 	*b;
1268 
1269 	bloblen += sizeof (krbapreq);
1270 	bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen;
1271 	len = bloblen;
1272 	bloblen = ASNDerCalcTokenLength(bloblen, bloblen);
1273 	failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok malloc");
1274 	if (!(blob = malloc(bloblen)))
1275 		goto out;
1276 	b = blob;
1277 	b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len);
1278 	b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5);
1279 	memcpy(b, krbapreq, sizeof (krbapreq));
1280 	b += sizeof (krbapreq);
1281 	failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok insanity check");
1282 	if (b + tktlen != blob + bloblen)
1283 		goto out;
1284 	memcpy(b, tkt, tktlen);
1285 	*gtoklenp = bloblen;
1286 	*gtokp = blob;
1287 	failure = NULL;
1288 out:;
1289 	if (blob && failure)
1290 		free(blob);
1291 	return (failure);
1292 }
1293 
1294 
1295 /*
1296  * Initialization for Kerberos, pulled out of smb_ctx_principal2tkt.
1297  * This just gets our cached credentials, if we have any.
1298  * Based on the "klist" command.
1299  */
1300 char *
1301 smb_ctx_krb5init(struct smb_ctx *ctx)
1302 {
1303 	char *failure;
1304 	krb5_error_code	kerr;
1305 	krb5_context	kctx = NULL;
1306 	krb5_ccache 	kcc = NULL;
1307 	krb5_principal	kprin = NULL;
1308 
1309 	kerr = krb5_init_context(&kctx);
1310 	if (kerr) {
1311 		failure = "krb5_init_context";
1312 		goto out;
1313 	}
1314 	ctx->ct_krb5ctx = kctx;
1315 
1316 	/* non-default would instead use krb5_cc_resolve */
1317 	kerr = krb5_cc_default(kctx, &kcc);
1318 	if (kerr) {
1319 		failure = "krb5_cc_default";
1320 		goto out;
1321 	}
1322 	ctx->ct_krb5cc = kcc;
1323 
1324 	/*
1325 	 * Get the client principal (ticket),
1326 	 * or find out if we don't have one.
1327 	 */
1328 	kerr = krb5_cc_get_principal(kctx, kcc, &kprin);
1329 	if (kerr) {
1330 		failure = "krb5_cc_get_principal";
1331 		goto out;
1332 	}
1333 	ctx->ct_krb5cp = kprin;
1334 
1335 	if (smb_verbose) {
1336 		fprintf(stderr, gettext("Ticket cache: %s:%s\n"),
1337 		    krb5_cc_get_type(kctx, kcc),
1338 		    krb5_cc_get_name(kctx, kcc));
1339 	}
1340 	failure = NULL;
1341 
1342 out:
1343 	return (failure);
1344 }
1345 
1346 
1347 /*
1348  * See "Windows 2000 Kerberos Interoperability" paper by
1349  * Christopher Nebergall.  RC4 HMAC is the W2K default but
1350  * Samba support lagged (not due to Samba itself, but due to OS'
1351  * Kerberos implementations.)
1352  *
1353  * Only session enc type should matter, not ticket enc type,
1354  * per Sam Hartman on krbdev.
1355  *
1356  * Preauthentication failure topics in krb-protocol may help here...
1357  * try "John Brezak" and/or "Clifford Neuman" too.
1358  */
1359 static krb5_enctype kenctypes[] = {
1360 	ENCTYPE_ARCFOUR_HMAC,	/* defined in Tiger krb5.h */
1361 	ENCTYPE_DES_CBC_MD5,
1362 	ENCTYPE_DES_CBC_CRC,
1363 	ENCTYPE_NULL
1364 };
1365 
1366 /*
1367  * Obtain a kerberos ticket...
1368  * (if TLD != "gov" then pray first)
1369  */
1370 char *
1371 smb_ctx_principal2tkt(
1372 	struct smb_ctx *ctx, char *prin,
1373 	uchar_t **tktp, ulong_t *tktlenp)
1374 {
1375 	char 		*failure;
1376 	krb5_context	kctx = NULL;
1377 	krb5_error_code	kerr;
1378 	krb5_ccache	kcc = NULL;
1379 	krb5_principal	kprin = NULL, cprn = NULL;
1380 	krb5_creds	kcreds, *kcredsp = NULL;
1381 	krb5_auth_context	kauth = NULL;
1382 	krb5_data	kdata, kdata0;
1383 	uchar_t 		*tkt;
1384 
1385 	memset((char *)&kcreds, 0, sizeof (kcreds));
1386 	kdata0.length = 0;
1387 
1388 	/* These shoud have been done in smb_ctx_krb5init() */
1389 	if (ctx->ct_krb5ctx == NULL ||
1390 	    ctx->ct_krb5cc == NULL ||
1391 	    ctx->ct_krb5cp == NULL) {
1392 		failure = "smb_ctx_krb5init";
1393 		goto out;
1394 	}
1395 	kctx = ctx->ct_krb5ctx;
1396 	kcc  = ctx->ct_krb5cc;
1397 	cprn = ctx->ct_krb5cp;
1398 
1399 	failure = "krb5_set_default_tgs_enctypes";
1400 	if ((kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes)))
1401 		goto out;
1402 	/*
1403 	 * The following is an unrolling of krb5_mk_req.  Something like:
1404 	 * krb5_mk_req(kctx, &kauth, 0, service(prin), hostname(prin),
1405 	 *		&kdata0, kcc, &kdata);)
1406 	 * ...except we needed krb5_parse_name not krb5_sname_to_principal.
1407 	 */
1408 	failure = "krb5_parse_name";
1409 	if ((kerr = krb5_parse_name(kctx, prin, &kprin)))
1410 		goto out;
1411 	failure = "krb5_copy_principal(server)";
1412 	if ((kerr = krb5_copy_principal(kctx, kprin, &kcreds.server)))
1413 		goto out;
1414 	failure = "krb5_copy_principal(client)";
1415 	if ((kerr = krb5_copy_principal(kctx, cprn, &kcreds.client)))
1416 		goto out;
1417 	failure = "krb5_get_credentials";
1418 	if ((kerr = krb5_get_credentials(kctx, 0, kcc, &kcreds, &kcredsp)))
1419 		goto out;
1420 	failure = "krb5_mk_req_extended";
1421 	if ((kerr = krb5_mk_req_extended(kctx, &kauth, 0, &kdata0, kcredsp,
1422 	    &kdata)))
1423 		goto out;
1424 	failure = "malloc";
1425 	if (!(tkt = malloc(kdata.length))) {
1426 		krb5_free_data_contents(kctx, &kdata);
1427 		goto out;
1428 	}
1429 	*tktlenp = kdata.length;
1430 	memcpy(tkt, kdata.data, kdata.length);
1431 	krb5_free_data_contents(kctx, &kdata);
1432 	*tktp = tkt;
1433 	failure = NULL;
1434 out:;
1435 	if (kerr) {
1436 		if (!failure)
1437 			failure = "smb_ctx_principal2tkt";
1438 		/*
1439 		 * Avoid logging the typical "No credentials cache found"
1440 		 */
1441 		if (kerr != KRB5_FCC_NOFILE ||
1442 		    strcmp(failure, "krb5_cc_get_principal"))
1443 			com_err(__progname, kerr, failure);
1444 	}
1445 	if (kauth)
1446 		krb5_auth_con_free(kctx, kauth);
1447 	if (kcredsp)
1448 		krb5_free_creds(kctx, kcredsp);
1449 	if (kcreds.server || kcreds.client)
1450 		krb5_free_cred_contents(kctx, &kcreds);
1451 	if (kprin)
1452 		krb5_free_principal(kctx, kprin);
1453 
1454 	/* Free kctx in smb_ctx_done */
1455 
1456 	return (failure);
1457 }
1458 
1459 char *
1460 smb_ctx_principal2blob(
1461 	struct smb_ctx *ctx,
1462 	smbioc_ossn_t *ssn,
1463 	char *prin)
1464 {
1465 	int		rc = 0;
1466 	char 		*failure;
1467 	uchar_t 	*tkt = NULL;
1468 	ulong_t		tktlen;
1469 	uchar_t 	*gtok = NULL;		/* gssapi token */
1470 	ulong_t		gtoklen;		/* gssapi token length */
1471 	SPNEGO_TOKEN_HANDLE  stok = NULL;	/* spnego token */
1472 	void 	*blob = NULL;		/* result */
1473 	ulong_t		bloblen;		/* result length */
1474 
1475 	if ((failure = smb_ctx_principal2tkt(ctx, prin, &tkt, &tktlen)))
1476 		goto out;
1477 	if ((failure = smb_ctx_tkt2gtok(tkt, tktlen, &gtok, &gtoklen)))
1478 		goto out;
1479 	/*
1480 	 * RFC says to send NegTokenTarg now.  So does MS docs.  But
1481 	 * win2k gives ERRbaduid if we do...  we must send
1482 	 * another NegTokenInit now!
1483 	 */
1484 	failure = "spnegoCreateNegTokenInit";
1485 	if ((rc = spnegoCreateNegTokenInit(spnego_mech_oid_Kerberos_V5_Legacy,
1486 	    0, gtok, gtoklen, NULL, 0, &stok)))
1487 		goto out;
1488 	failure = "spnegoTokenGetBinary(NULL)";
1489 	rc = spnegoTokenGetBinary(stok, NULL, &bloblen);
1490 	if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
1491 		goto out;
1492 	failure = "malloc";
1493 	if (!(blob = malloc((size_t)bloblen)))
1494 		goto out;
1495 	/* No longer store length at start of blob. */
1496 	/* *blob = bloblen; */
1497 	failure = "spnegoTokenGetBinary";
1498 	if ((rc = spnegoTokenGetBinary(stok, blob, &bloblen)))
1499 		goto out;
1500 	ssn->ioc_intoklen = bloblen;
1501 	ssn->ioc_intok = blob;
1502 	failure = NULL;
1503 out:;
1504 	if (rc) {
1505 		/* XXX better is to embed rc in failure */
1506 		smb_error(dgettext(TEXT_DOMAIN,
1507 		    "spnego principal2blob error %d"), 0, -rc);
1508 		if (!failure)
1509 			failure = "spnego";
1510 	}
1511 	if (blob && failure)
1512 		free(blob);
1513 	if (stok)
1514 		spnegoFreeData(stok);
1515 	if (gtok)
1516 		free(gtok);
1517 	if (tkt)
1518 		free(tkt);
1519 	return (failure);
1520 }
1521 
1522 
1523 #if 0
1524 void
1525 prblob(uchar_t *b, size_t len)
1526 {
1527 	while (len--)
1528 		fprintf(stderr, "%02x", *b++);
1529 	fprintf(stderr, "\n");
1530 }
1531 #endif
1532 
1533 
1534 /*
1535  * We navigate the SPNEGO & ASN1 encoding to find a kerberos principal
1536  * Note: driver no longer puts length at start of blob.
1537  */
1538 char *
1539 smb_ctx_blob2principal(
1540 	struct smb_ctx *ctx,
1541 	smbioc_ossn_t *ssn,
1542 	char **prinp)
1543 {
1544 	uchar_t		*blob = ssn->ioc_outtok;
1545 	size_t		len = ssn->ioc_outtoklen;
1546 	int		rc = 0;
1547 	SPNEGO_TOKEN_HANDLE	stok = NULL;
1548 	int		indx = 0;
1549 	char 		*failure;
1550 	uchar_t		flags = 0;
1551 	unsigned long	plen = 0;
1552 	uchar_t 	*prin;
1553 
1554 #if 0
1555 	fprintf(stderr, "blob from negotiate:\n");
1556 	prblob(blob, len);
1557 #endif
1558 
1559 	/* Skip the GUID */
1560 	assert(len >= SMB_GUIDLEN);
1561 	blob += SMB_GUIDLEN;
1562 	len  -= SMB_GUIDLEN;
1563 
1564 	failure = "spnegoInitFromBinary";
1565 	if ((rc = spnegoInitFromBinary(blob, len, &stok)))
1566 		goto out;
1567 	/*
1568 	 * Needn't use new Kerberos OID - the Legacy one is fine.
1569 	 */
1570 	failure = "spnegoIsMechTypeAvailable";
1571 	if (spnegoIsMechTypeAvailable(stok, spnego_mech_oid_Kerberos_V5_Legacy,
1572 	    &indx))
1573 		goto out;
1574 	/*
1575 	 * Ignoring optional context flags for now.  May want to pass
1576 	 * them to krb5 layer.  XXX
1577 	 */
1578 	if (!spnegoGetContextFlags(stok, &flags))
1579 		fprintf(stderr, dgettext(TEXT_DOMAIN,
1580 		    "spnego context flags 0x%x\n"), flags);
1581 	failure = "spnegoGetMechListMIC(NULL)";
1582 	rc = spnegoGetMechListMIC(stok, NULL, &plen);
1583 	if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
1584 		goto out;
1585 	failure = "malloc";
1586 	if (!(prin = malloc(plen + 1)))
1587 		goto out;
1588 	failure = "spnegoGetMechListMIC";
1589 	if ((rc = spnegoGetMechListMIC(stok, prin, &plen))) {
1590 		free(prin);
1591 		goto out;
1592 	}
1593 	prin[plen] = '\0';
1594 	*prinp = (char *)prin;
1595 	failure = NULL;
1596 out:;
1597 	if (stok)
1598 		spnegoFreeData(stok);
1599 	if (rc) {
1600 		/* XXX better is to embed rc in failure */
1601 		smb_error(dgettext(TEXT_DOMAIN,
1602 		    "spnego blob2principal error %d"), 0, -rc);
1603 		if (!failure)
1604 			failure = "spnego";
1605 	}
1606 	return (failure);
1607 }
1608 
1609 
1610 int
1611 smb_ctx_negotiate(struct smb_ctx *ctx, int level, int flags, char *workgroup)
1612 {
1613 	struct smbioc_lookup	rq;
1614 	int	error = 0;
1615 	char 	*failure = NULL;
1616 	char	*principal = NULL;
1617 	char c;
1618 	int i;
1619 	ssize_t *outtoklen;
1620 	uchar_t *blob;
1621 
1622 	/*
1623 	 * We leave ct_secblob set iff extended security
1624 	 * negotiation succeeds.
1625 	 */
1626 	if (ctx->ct_secblob) {
1627 		free(ctx->ct_secblob);
1628 		ctx->ct_secblob = NULL;
1629 	}
1630 #ifdef XXX
1631 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
1632 		smb_error(dgettext(TEXT_DOMAIN,
1633 		    "smb_ctx_lookup() data is not resolved"), 0);
1634 		return (EINVAL);
1635 	}
1636 #endif
1637 	if ((error = smb_ctx_gethandle(ctx)))
1638 		return (error);
1639 
1640 	bzero(&rq, sizeof (rq));
1641 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1642 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1643 
1644 	/*
1645 	 * Find out if we have a Kerberos ticket,
1646 	 * and only offer SPNEGO if we have one.
1647 	 */
1648 	failure = smb_ctx_krb5init(ctx);
1649 	if (failure) {
1650 		if (smb_verbose)
1651 			smb_error(failure, 0);
1652 		goto out;
1653 	}
1654 
1655 	rq.ioc_flags = flags;
1656 	rq.ioc_level = level;
1657 	rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
1658 	error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
1659 	if (error) {
1660 		failure = dgettext(TEXT_DOMAIN, "negotiate failed");
1661 		smb_error(failure, error);
1662 		if (error == ETIMEDOUT)
1663 			return (error);
1664 		goto out;
1665 	}
1666 	/*
1667 	 * If the server capabilities did not include
1668 	 * SMB_CAP_EXT_SECURITY then the driver clears
1669 	 * the flag SMBVOPT_EXT_SEC for us.
1670 	 * XXX: should add the capabilities to ioc_ssn
1671 	 * XXX: see comment in driver - smb_usr.c
1672 	 */
1673 	failure = dgettext(TEXT_DOMAIN, "SPNEGO unsupported");
1674 	if ((rq.ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC) == 0) {
1675 		if (smb_verbose)
1676 			smb_error(failure, 0);
1677 		/*
1678 		 * Do regular (old style) NTLM or NTLMv2
1679 		 * Nothing more to do here in negotiate.
1680 		 */
1681 		return (0);
1682 	}
1683 
1684 	/*
1685 	 * Capabilities DO include SMB_CAP_EXT_SECURITY,
1686 	 * so this should be an SPNEGO security blob.
1687 	 * Parse the ASN.1/DER, prepare response(s).
1688 	 * XXX: Handle STATUS_MORE_PROCESSING_REQUIRED?
1689 	 * XXX: Requires additional session setup calls.
1690 	 */
1691 	if (rq.ioc_ssn.ioc_outtoklen <= SMB_GUIDLEN)
1692 		goto out;
1693 	/* some servers send padding junk */
1694 	blob = rq.ioc_ssn.ioc_outtok;
1695 	if (blob[0] == 0)
1696 		goto out;
1697 
1698 	failure = smb_ctx_blob2principal(
1699 	    ctx, &rq.ioc_ssn, &principal);
1700 	if (failure)
1701 		goto out;
1702 	failure = smb_ctx_principal2blob(
1703 	    ctx, &rq.ioc_ssn, principal);
1704 	if (failure)
1705 		goto out;
1706 
1707 	/* Success! Save the blob to send next. */
1708 	ctx->ct_secblob = rq.ioc_ssn.ioc_intok;
1709 	ctx->ct_secbloblen = rq.ioc_ssn.ioc_intoklen;
1710 	rq.ioc_ssn.ioc_intok = NULL;
1711 
1712 out:
1713 	if (principal)
1714 		free(principal);
1715 	if (rq.ioc_ssn.ioc_intok)
1716 		free(rq.ioc_ssn.ioc_intok);
1717 	if (rq.ioc_ssn.ioc_outtok)
1718 		free(rq.ioc_ssn.ioc_outtok);
1719 	if (!failure)
1720 		return (0);		/* Success! */
1721 
1722 	/*
1723 	 * Negotiate failed with "extended security".
1724 	 *
1725 	 * XXX: If we are doing SPNEGO correctly,
1726 	 * we should never get here unless the user
1727 	 * supplied invalid authentication data,
1728 	 * or we saw some kind of protocol error.
1729 	 *
1730 	 * XXX: The error message below should be
1731 	 * XXX: unconditional (remove "if verbose")
1732 	 * XXX: but not until we have "NTLMSSP"
1733 	 * Avoid spew for anticipated failure modes
1734 	 * but enable this with the verbose flag
1735 	 */
1736 	if (smb_verbose) {
1737 		smb_error(dgettext(TEXT_DOMAIN,
1738 		    "%s (extended security negotiate)"), error, failure);
1739 	}
1740 
1741 	/*
1742 	 * XXX: Try again using NTLM (or NTLMv2)
1743 	 * XXX: Normal clients don't do this.
1744 	 * XXX: Should just return an error, but
1745 	 * keep the fall-back to NTLM for now.
1746 	 *
1747 	 * Start over with a new connection.
1748 	 */
1749 	if ((error = smb_ctx_gethandle(ctx)))
1750 		return (error);
1751 	bzero(&rq, sizeof (rq));
1752 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1753 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1754 	rq.ioc_flags = flags;
1755 	rq.ioc_level = level;
1756 	/* Note: NO SMBVOPT_EXT_SEC */
1757 	error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
1758 	if (error) {
1759 		failure = dgettext(TEXT_DOMAIN, "negotiate failed");
1760 		smb_error(failure, error);
1761 		rpc_cleanup_smbctx(ctx);
1762 		close(ctx->ct_fd);
1763 		ctx->ct_fd = -1;
1764 		return (error);
1765 	}
1766 
1767 	/*
1768 	 * Used to copy the workgroup out of the SMB_NEGOTIATE response
1769 	 * here, to default our domain name to be the same as the server.
1770 	 * Not a good idea: Unnecessary at best, and sometimes wrong, i.e.
1771 	 * when our account is in a trusted domain.
1772 	 */
1773 
1774 	return (error);
1775 }
1776 
1777 
1778 int
1779 smb_ctx_tdis(struct smb_ctx *ctx)
1780 {
1781 	struct smbioc_lookup rq; /* XXX may be used, someday */
1782 	int error = 0;
1783 
1784 	if (ctx->ct_fd < 0) {
1785 		smb_error(dgettext(TEXT_DOMAIN,
1786 		    "tree disconnect without handle?!"), 0);
1787 		return (EINVAL);
1788 	}
1789 	if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
1790 		smb_error(dgettext(TEXT_DOMAIN,
1791 		    "tree disconnect without session?!"), 0);
1792 		return (EINVAL);
1793 	}
1794 	bzero(&rq, sizeof (rq));
1795 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1796 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1797 	if (ioctl(ctx->ct_fd, SMBIOC_TDIS, &rq) == -1) {
1798 		error = errno;
1799 		smb_error(dgettext(TEXT_DOMAIN,
1800 		    "tree disconnect failed"), error);
1801 	}
1802 	return (error);
1803 }
1804 
1805 
1806 int
1807 smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
1808 {
1809 	struct smbioc_lookup rq;
1810 	int error = 0;
1811 	char 	*failure = NULL;
1812 
1813 	if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
1814 		smb_error(dgettext(TEXT_DOMAIN,
1815 		    "smb_ctx_lookup() data is not resolved"), 0);
1816 		return (EINVAL);
1817 	}
1818 	if (ctx->ct_fd < 0) {
1819 		smb_error(dgettext(TEXT_DOMAIN,
1820 		    "handle from smb_ctx_nego() gone?!"), 0);
1821 		return (EINVAL);
1822 	}
1823 	if (!(flags & SMBLK_CREATE))
1824 		return (0);
1825 	bzero(&rq, sizeof (rq));
1826 	bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
1827 	bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
1828 	rq.ioc_flags = flags;
1829 	rq.ioc_level = level;
1830 
1831 	/*
1832 	 * Iff we have a security blob, we're using
1833 	 * extended security...
1834 	 */
1835 	if (ctx->ct_secblob) {
1836 		rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
1837 		if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
1838 			rq.ioc_ssn.ioc_intok = ctx->ct_secblob;
1839 			rq.ioc_ssn.ioc_intoklen = ctx->ct_secbloblen;
1840 			error = smb_ctx_ioctl(ctx, SMBIOC_SSNSETUP, &rq);
1841 		}
1842 		rq.ioc_ssn.ioc_intok = NULL;
1843 		if (error) {
1844 			failure = dgettext(TEXT_DOMAIN,
1845 			    "session setup failed");
1846 		} else {
1847 			ctx->ct_flags |= SMBCF_SSNACTIVE;
1848 			if ((error = smb_ctx_ioctl(ctx, SMBIOC_TCON, &rq)))
1849 				failure = dgettext(TEXT_DOMAIN,
1850 				    "tree connect failed");
1851 		}
1852 		if (rq.ioc_ssn.ioc_intok)
1853 			free(rq.ioc_ssn.ioc_intok);
1854 		if (rq.ioc_ssn.ioc_outtok)
1855 			free(rq.ioc_ssn.ioc_outtok);
1856 		if (!failure)
1857 			return (0);
1858 		smb_error(dgettext(TEXT_DOMAIN,
1859 		    "%s (extended security lookup2)"), error, failure);
1860 		/* unwise to failback to NTLM now */
1861 		return (error);
1862 	}
1863 
1864 	/*
1865 	 * Otherwise we're doing plain old NTLM
1866 	 */
1867 	seteuid(eff_uid); /* restore setuid root briefly */
1868 	if ((ctx->ct_flags & SMBCF_SSNACTIVE) == 0) {
1869 		/*
1870 		 * This is the magic that tells the driver to
1871 		 * copy the password from the keychain, and
1872 		 * whether to use the system name or the
1873 		 * account domain to lookup the keychain.
1874 		 */
1875 		if (ctx->ct_flags & SMBCF_KCFOUND)
1876 			rq.ioc_ssn.ioc_opt |= SMBVOPT_USE_KEYCHAIN;
1877 		if (ctx->ct_flags & SMBCF_KCDOMAIN)
1878 			rq.ioc_ssn.ioc_opt |= SMBVOPT_KC_DOMAIN;
1879 		if (ioctl(ctx->ct_fd, SMBIOC_SSNSETUP, &rq) < 0) {
1880 			error = errno;
1881 			failure = dgettext(TEXT_DOMAIN, "session setup");
1882 			goto out;
1883 		}
1884 		ctx->ct_flags |= SMBCF_SSNACTIVE;
1885 	}
1886 	if (ioctl(ctx->ct_fd, SMBIOC_TCON, &rq) == -1) {
1887 		error = errno;
1888 		failure = dgettext(TEXT_DOMAIN, "tree connect");
1889 	}
1890 
1891 out:
1892 	seteuid(real_uid); /* and back to real user */
1893 	if (failure) {
1894 		error = errno;
1895 		smb_error(dgettext(TEXT_DOMAIN,
1896 		    "%s phase failed"), error, failure);
1897 	}
1898 	return (error);
1899 }
1900 
1901 /*
1902  * Return the hflags2 word for an smb_ctx.
1903  */
1904 int
1905 smb_ctx_flags2(struct smb_ctx *ctx)
1906 {
1907 	uint16_t flags2;
1908 
1909 	if (ioctl(ctx->ct_fd, SMBIOC_FLAGS2, &flags2) == -1) {
1910 		smb_error(dgettext(TEXT_DOMAIN,
1911 		    "can't get flags2 for a session"), errno);
1912 		return (-1);
1913 	}
1914 	printf(dgettext(TEXT_DOMAIN, "Flags2 value is %d\n"), flags2);
1915 	return (flags2);
1916 }
1917 
1918 /*
1919  * level values:
1920  * 0 - default
1921  * 1 - server
1922  * 2 - server:user
1923  * 3 - server:user:share
1924  */
1925 static int
1926 smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
1927 {
1928 	char *p;
1929 	int error;
1930 
1931 #ifdef NOT_DEFINED
1932 	if (level > 0) {
1933 		rc_getstringptr(smb_rc, sname, "charsets", &p);
1934 		if (p) {
1935 			error = smb_ctx_setcharset(ctx, p);
1936 			if (error)
1937 				smb_error(dgettext(TEXT_DOMAIN,
1938 	"charset specification in the section '%s' ignored"),
1939 				    error, sname);
1940 		}
1941 	}
1942 #endif
1943 
1944 	if (level <= 1) {
1945 		/* Section is: [default] or [server] */
1946 
1947 		rc_getint(smb_rc, sname, "timeout",
1948 		    &ctx->ct_ssn.ioc_timeout);
1949 
1950 #ifdef NOT_DEFINED
1951 		rc_getint(smb_rc, sname, "retry_count",
1952 		    &ctx->ct_ssn.ioc_retrycount);
1953 		rc_getstringptr(smb_rc, sname, "use_negprot_domain", &p);
1954 		if (p && strcmp(p, "NO") == 0)
1955 			ctx->ct_flags |= SMBCF_NONEGDOM;
1956 #endif
1957 
1958 		rc_getstringptr(smb_rc, sname, "minauth", &p);
1959 		if (p) {
1960 			/*
1961 			 * "minauth" was set in this section; override
1962 			 * the current minimum authentication setting.
1963 			 */
1964 			ctx->ct_ssn.ioc_opt &= ~SMBVOPT_MINAUTH;
1965 			if (strcmp(p, "kerberos") == 0) {
1966 				/*
1967 				 * Don't fall back to NTLMv2, NTLMv1, or
1968 				 * a clear text password.
1969 				 */
1970 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_KERBEROS;
1971 			} else if (strcmp(p, "ntlmv2") == 0) {
1972 				/*
1973 				 * Don't fall back to NTLMv1 or a clear
1974 				 * text password.
1975 				 */
1976 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLMV2;
1977 			} else if (strcmp(p, "ntlm") == 0) {
1978 				/*
1979 				 * Don't send the LM response over the wire.
1980 				 */
1981 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLM;
1982 			} else if (strcmp(p, "lm") == 0) {
1983 				/*
1984 				 * Fail if the server doesn't do encrypted
1985 				 * passwords.
1986 				 */
1987 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_LM;
1988 			} else if (strcmp(p, "none") == 0) {
1989 				/*
1990 				 * Anything goes.
1991 				 * (The following statement should be
1992 				 * optimized away.)
1993 				 */
1994 				/* LINTED */
1995 				ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NONE;
1996 			} else {
1997 				/*
1998 				 * Unknown minimum authentication level.
1999 				 */
2000 				smb_error(dgettext(TEXT_DOMAIN,
2001 "invalid minimum authentication level \"%s\" specified in the section %s"),
2002 				    0, p, sname);
2003 				return (EINVAL);
2004 			}
2005 		}
2006 
2007 		/*
2008 		 * Domain name.  Allow both keywords:
2009 		 * "workgroup", "domain"
2010 		 *
2011 		 * Note: these are NOT marked "from CMD".
2012 		 * See long comment at smb_ctx_init()
2013 		 */
2014 		rc_getstringptr(smb_rc, sname, "workgroup", &p);
2015 		if (p) {
2016 			nls_str_upper(p, p);
2017 			error = smb_ctx_setworkgroup(ctx, p, 0);
2018 			if (error)
2019 				smb_error(dgettext(TEXT_DOMAIN,
2020 				    "workgroup specification in the "
2021 				    "section '%s' ignored"), error, sname);
2022 		}
2023 		rc_getstringptr(smb_rc, sname, "domain", &p);
2024 		if (p) {
2025 			nls_str_upper(p, p);
2026 			error = smb_ctx_setworkgroup(ctx, p, 0);
2027 			if (error)
2028 				smb_error(dgettext(TEXT_DOMAIN,
2029 				    "domain specification in the "
2030 				    "section '%s' ignored"), error, sname);
2031 		}
2032 
2033 		rc_getstringptr(smb_rc, sname, "user", &p);
2034 		if (p) {
2035 			error = smb_ctx_setuser(ctx, p, 0);
2036 			if (error)
2037 				smb_error(dgettext(TEXT_DOMAIN,
2038 				    "user specification in the "
2039 				    "section '%s' ignored"), error, sname);
2040 		}
2041 	}
2042 
2043 	if (level == 1) {
2044 		/* Section is: [server] */
2045 		rc_getstringptr(smb_rc, sname, "addr", &p);
2046 		if (p) {
2047 			error = smb_ctx_setsrvaddr(ctx, p);
2048 			if (error) {
2049 				smb_error(dgettext(TEXT_DOMAIN,
2050 				    "invalid address specified in section %s"),
2051 				    0, sname);
2052 				return (error);
2053 			}
2054 		}
2055 	}
2056 
2057 	rc_getstringptr(smb_rc, sname, "password", &p);
2058 	if (p) {
2059 		error = smb_ctx_setpassword(ctx, p, 0);
2060 		if (error)
2061 			smb_error(dgettext(TEXT_DOMAIN,
2062 	    "password specification in the section '%s' ignored"),
2063 			    error, sname);
2064 	}
2065 
2066 	return (0);
2067 }
2068 
2069 /*
2070  * read rc file as follows:
2071  * 0: read [default] section
2072  * 1: override with [server] section
2073  * 2: override with [server:user] section
2074  * 3: override with [server:user:share] section
2075  * Since absence of rcfile is not fatal, silently ignore this fact.
2076  * smb_rc file should be closed by caller.
2077  */
2078 int
2079 smb_ctx_readrc(struct smb_ctx *ctx)
2080 {
2081 	char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN +
2082 	    SMB_MAXSHARENAMELEN + 4];
2083 
2084 	if (smb_open_rcfile(ctx) != 0)
2085 		goto done;
2086 
2087 	/*
2088 	 * default parameters (level=0)
2089 	 */
2090 	smb_ctx_readrcsection(ctx, "default", 0);
2091 	nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
2092 
2093 	/*
2094 	 * If we don't have a server name, we can't read any of the
2095 	 * [server...] sections.
2096 	 */
2097 	if (ctx->ct_ssn.ioc_srvname[0] == 0)
2098 		goto done;
2099 
2100 	/*
2101 	 * SERVER parameters.
2102 	 */
2103 	smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
2104 
2105 	/*
2106 	 * If we don't have a user name, we can't read any of the
2107 	 * [server:user...] sections.
2108 	 */
2109 	if (ctx->ct_ssn.ioc_user[0] == 0)
2110 		goto done;
2111 
2112 	/*
2113 	 * SERVER:USER parameters
2114 	 */
2115 	snprintf(sname, sizeof (sname), "%s:%s",
2116 	    ctx->ct_ssn.ioc_srvname,
2117 	    ctx->ct_ssn.ioc_user);
2118 	smb_ctx_readrcsection(ctx, sname, 2);
2119 
2120 	/*
2121 	 * If we don't have a share name, we can't read any of the
2122 	 * [server:user:share] sections.
2123 	 */
2124 	if (ctx->ct_sh.ioc_share[0] != 0) {
2125 		/*
2126 		 * SERVER:USER:SHARE parameters
2127 		 */
2128 		snprintf(sname, sizeof (sname), "%s:%s:%s",
2129 		    ctx->ct_ssn.ioc_srvname,
2130 		    ctx->ct_ssn.ioc_user,
2131 		    ctx->ct_sh.ioc_share);
2132 		smb_ctx_readrcsection(ctx, sname, 3);
2133 	}
2134 
2135 done:
2136 	if (smb_debug)
2137 		dump_ctx("after smb_ctx_readrc", ctx);
2138 
2139 	return (0);
2140 }
2141