xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c (revision fe54a78e1aacf39261ad56e9903bce02e3fb6d21)
1 /*
2  * Copyright (c) 2000-2001 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: smb_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $
33  */
34 /*
35  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
36  * Use is subject to license terms.
37  */
38 
39 /*
40  * Connection engine.
41  */
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kmem.h>
46 #include <sys/proc.h>
47 #include <sys/lock.h>
48 #include <sys/vnode.h>
49 #include <sys/stream.h>
50 #include <sys/stropts.h>
51 #include <sys/socketvar.h>
52 #include <sys/cred.h>
53 #include <sys/cred_impl.h>
54 #include <netinet/in.h>
55 #include <inet/ip.h>
56 #include <inet/ip6.h>
57 #include <sys/cmn_err.h>
58 #include <sys/thread.h>
59 #include <sys/atomic.h>
60 
61 #ifdef APPLE
62 #include <sys/smb_apple.h>
63 #include <sys/smb_iconv.h>
64 #else
65 #include <netsmb/smb_osdep.h>
66 #endif
67 
68 #include <netsmb/smb.h>
69 #include <netsmb/smb_conn.h>
70 #include <netsmb/smb_subr.h>
71 #include <netsmb/smb_tran.h>
72 #include <netsmb/smb_pass.h>
73 
74 static struct smb_connobj smb_vclist;
75 static uint_t smb_vcnext = 0;	/* next unique id for VC */
76 
77 void smb_co_init(struct smb_connobj *cp, int level, char *objname);
78 void smb_co_done(struct smb_connobj *cp);
79 void smb_co_hold(struct smb_connobj *cp);
80 void smb_co_rele(struct smb_connobj *cp);
81 void smb_co_kill(struct smb_connobj *cp);
82 
83 #ifdef APPLE
84 static void smb_sm_lockvclist(void);
85 static void smb_sm_unlockvclist(void);
86 #endif
87 
88 static void smb_vc_free(struct smb_connobj *cp);
89 static void smb_vc_gone(struct smb_connobj *cp);
90 
91 static void smb_share_free(struct smb_connobj *cp);
92 static void smb_share_gone(struct smb_connobj *cp);
93 
94 /* smb_dup_sockaddr moved to smb_tran.c */
95 
96 int
97 smb_sm_init(void)
98 {
99 	smb_co_init(&smb_vclist, SMBL_SM, "smbsm");
100 	return (0);
101 }
102 
103 int
104 smb_sm_idle(void)
105 {
106 	int error = 0;
107 	SMB_CO_LOCK(&smb_vclist);
108 	if (smb_vclist.co_usecount > 1) {
109 		SMBSDEBUG("%d connections still active\n",
110 		    smb_vclist.co_usecount - 1);
111 		error = EBUSY;
112 	}
113 	SMB_CO_UNLOCK(&smb_vclist);
114 	return (error);
115 }
116 
117 void
118 smb_sm_done(void)
119 {
120 	/*
121 	 * XXX Q4BP why are we not iterating on smb_vclist here?
122 	 * Because the caller has just called smb_sm_idle() to
123 	 * make sure we have no VCs before calling this.
124 	 */
125 	smb_co_done(&smb_vclist);
126 }
127 
128 /*
129  * Find a VC identified by the info in vcspec,
130  * and return it with a "hold", but not locked.
131  */
132 /*ARGSUSED*/
133 static int
134 smb_sm_lookupvc(
135 	struct smb_vcspec *vcspec,
136 	struct smb_cred *scred,
137 	struct smb_vc **vcpp)
138 {
139 	struct smb_connobj *co;
140 	struct smb_vc *vcp;
141 	zoneid_t zoneid = getzoneid();
142 
143 	ASSERT(MUTEX_HELD(&smb_vclist.co_lock));
144 
145 	/* var, head, next_field */
146 	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
147 		vcp = CPTOVC(co);
148 
149 		/*
150 		 * Some things we can check without
151 		 * holding the lock (those that are
152 		 * set at creation and never change).
153 		 */
154 
155 		/* VCs in other zones are invisibile. */
156 		if (vcp->vc_zoneid != zoneid)
157 			continue;
158 
159 		/* Also segregate by owner. */
160 		if (vcp->vc_uid != vcspec->owner)
161 			continue;
162 
163 		/* XXX: we ignore the group.  Remove vc_gid? */
164 
165 		/* server */
166 		if (smb_cmp_sockaddr(vcp->vc_paddr, vcspec->sap))
167 			continue;
168 
169 		/* domain+user */
170 		if (strcmp(vcp->vc_domain, vcspec->domain))
171 			continue;
172 		if (strcmp(vcp->vc_username, vcspec->username))
173 			continue;
174 
175 		SMB_VC_LOCK(vcp);
176 
177 		/* No new references allowed when _GONE is set */
178 		if (vcp->vc_flags & SMBV_GONE)
179 			goto unlock_continue;
180 
181 		if (vcp->vc_vopt & SMBVOPT_PRIVATE)
182 			goto unlock_continue;
183 
184 	found:
185 		/*
186 		 * Success! (Found one we can use)
187 		 * Return with it held, unlocked.
188 		 * In-line smb_vc_hold here.
189 		 */
190 		co->co_usecount++;
191 		SMB_VC_UNLOCK(vcp);
192 		*vcpp = vcp;
193 		return (0);
194 
195 	unlock_continue:
196 		SMB_VC_UNLOCK(vcp);
197 		/* keep looking. */
198 	}
199 
200 	return (ENOENT);
201 }
202 
203 int
204 smb_sm_findvc(
205 	struct smb_vcspec *vcspec,
206 	struct smb_cred *scred,
207 	struct smb_vc **vcpp)
208 {
209 	struct smb_vc *vcp;
210 	int error;
211 
212 	*vcpp = vcp = NULL;
213 
214 	SMB_CO_LOCK(&smb_vclist);
215 	error = smb_sm_lookupvc(vcspec, scred, &vcp);
216 	SMB_CO_UNLOCK(&smb_vclist);
217 
218 	/* Return if smb_sm_lookupvc fails */
219 	if (error != 0)
220 		return (error);
221 
222 	/* Ingore any VC that's not active. */
223 	if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
224 		smb_vc_rele(vcp);
225 		return (ENOENT);
226 	}
227 
228 	/* Active VC. Return it held. */
229 	*vcpp = vcp;
230 	return (error);
231 }
232 
233 int
234 smb_sm_negotiate(
235 	struct smb_vcspec *vcspec,
236 	struct smb_cred *scred,
237 	struct smb_vc **vcpp)
238 {
239 	struct smb_vc *vcp;
240 	clock_t tmo;
241 	int created, error;
242 
243 top:
244 	*vcpp = vcp = NULL;
245 
246 	SMB_CO_LOCK(&smb_vclist);
247 	error = smb_sm_lookupvc(vcspec, scred, &vcp);
248 	if (error) {
249 		/* The VC was not found.  Create? */
250 		if ((vcspec->optflags & SMBVOPT_CREATE) == 0) {
251 			SMB_CO_UNLOCK(&smb_vclist);
252 			return (error);
253 		}
254 		error = smb_vc_create(vcspec, scred, &vcp);
255 		if (error) {
256 			/* Could not create? Unusual. */
257 			SMB_CO_UNLOCK(&smb_vclist);
258 			return (error);
259 		}
260 		/* Note: co_usecount == 1 */
261 		created = 1;
262 	} else
263 		created = 0;
264 	SMB_CO_UNLOCK(&smb_vclist);
265 
266 	if (created == 0) {
267 		/*
268 		 * Found an existing VC.  Reuse it, but first,
269 		 * wait for any other thread doing setup, etc.
270 		 * Note: We hold a reference on the VC.
271 		 */
272 		error = 0;
273 		SMB_VC_LOCK(vcp);
274 		while (vcp->vc_state < SMBIOD_ST_VCACTIVE) {
275 			if (vcp->vc_flags & SMBV_GONE)
276 				break;
277 			tmo = lbolt + SEC_TO_TICK(2);
278 			tmo = cv_timedwait_sig(&vcp->vc_statechg,
279 			    &vcp->vc_lock, tmo);
280 			if (tmo == 0) {
281 				error = EINTR;
282 				break;
283 			}
284 		}
285 		SMB_VC_UNLOCK(vcp);
286 
287 		/* Interrupted? */
288 		if (error)
289 			goto out;
290 
291 		/*
292 		 * Was there a vc_kill while we waited?
293 		 * If so, this VC is gone.  Start over.
294 		 */
295 		if (vcp->vc_flags & SMBV_GONE) {
296 			smb_vc_rele(vcp);
297 			goto top;
298 		}
299 
300 		/*
301 		 * The possible states here are:
302 		 * SMBIOD_ST_VCACTIVE, SMBIOD_ST_DEAD
303 		 *
304 		 * SMBIOD_ST_VCACTIVE is the normal case,
305 		 * where found a connection ready to use.
306 		 *
307 		 * We may find vc_state == SMBIOD_ST_DEAD
308 		 * if a previous session has disconnected.
309 		 * In this case, we'd like to reconnect,
310 		 * so take over setting up this VC as if
311 		 * this thread had created it.
312 		 */
313 		SMB_VC_LOCK(vcp);
314 		if (vcp->vc_state == SMBIOD_ST_DEAD) {
315 			vcp->vc_state = SMBIOD_ST_NOTCONN;
316 			created = 1;
317 			/* Will signal vc_statechg below */
318 		}
319 		SMB_VC_UNLOCK(vcp);
320 	}
321 
322 	if (created) {
323 		/*
324 		 * We have a NEW VC, held, but not locked.
325 		 */
326 
327 		SMBIODEBUG("vc_state=%d\n", vcp->vc_state);
328 		switch (vcp->vc_state) {
329 
330 		case SMBIOD_ST_NOTCONN:
331 			(void) smb_vc_setup(vcspec, scred, vcp, 0);
332 			vcp->vc_genid++;
333 			/* XXX: Save credentials of caller here? */
334 			vcp->vc_state = SMBIOD_ST_RECONNECT;
335 			/* FALLTHROUGH */
336 
337 		case SMBIOD_ST_RECONNECT:
338 			error = smb_iod_connect(vcp);
339 			if (error)
340 				break;
341 			vcp->vc_state = SMBIOD_ST_TRANACTIVE;
342 			/* FALLTHROUGH */
343 
344 		case SMBIOD_ST_TRANACTIVE:
345 			/* XXX: Just pass vcspec instead? */
346 			vcp->vc_intok = vcspec->tok;
347 			vcp->vc_intoklen = vcspec->toklen;
348 			error = smb_smb_negotiate(vcp, &vcp->vc_scred);
349 			vcp->vc_intok = NULL;
350 			vcp->vc_intoklen = 0;
351 			if (error)
352 				break;
353 			vcp->vc_state = SMBIOD_ST_NEGOACTIVE;
354 			/* FALLTHROUGH */
355 
356 		case SMBIOD_ST_NEGOACTIVE:
357 		case SMBIOD_ST_SSNSETUP:
358 		case SMBIOD_ST_VCACTIVE:
359 			/* We can (re)use this VC. */
360 			error = 0;
361 			break;
362 
363 		default:
364 			error = EINVAL;
365 			break;
366 		}
367 
368 		if (error) {
369 			/*
370 			 * Leave the VC in a state that allows the
371 			 * next open to attempt a new connection.
372 			 * This call does the cv_broadcast too,
373 			 * so that's in the else part.
374 			 */
375 			smb_iod_disconnect(vcp);
376 		} else {
377 			SMB_VC_LOCK(vcp);
378 			cv_broadcast(&vcp->vc_statechg);
379 			SMB_VC_UNLOCK(vcp);
380 		}
381 	}
382 
383 out:
384 	if (error) {
385 		/*
386 		 * Undo the hold from lookupvc,
387 		 * or destroy if from vc_create.
388 		 */
389 		smb_vc_rele(vcp);
390 	} else {
391 		/* Return it held. */
392 		*vcpp = vcp;
393 	}
394 
395 	return (error);
396 }
397 
398 
399 int
400 smb_sm_ssnsetup(
401 	struct smb_vcspec *vcspec,
402 	struct smb_cred *scred,
403 	struct smb_vc *vcp)
404 {
405 	int error;
406 
407 	/*
408 	 * We have a VC, held, but not locked.
409 	 *
410 	 * Code from smb_iod_ssnsetup,
411 	 * with lots of rework.
412 	 */
413 
414 	SMBIODEBUG("vc_state=%d\n", vcp->vc_state);
415 	switch (vcp->vc_state) {
416 
417 	case SMBIOD_ST_NEGOACTIVE:
418 		/*
419 		 * This is the state we normally find.
420 		 * Calling _setup AGAIN to update the
421 		 * flags, security info, etc.
422 		 */
423 		error = smb_vc_setup(vcspec, scred, vcp, 1);
424 		if (error)
425 			break;
426 		vcp->vc_state = SMBIOD_ST_SSNSETUP;
427 		/* FALLTHROUGH */
428 
429 	case SMBIOD_ST_SSNSETUP:
430 		/* XXX: Just pass vcspec instead? */
431 		vcp->vc_intok = vcspec->tok;
432 		vcp->vc_intoklen = vcspec->toklen;
433 		error = smb_smb_ssnsetup(vcp, &vcp->vc_scred);
434 		vcp->vc_intok = NULL;
435 		vcp->vc_intoklen = 0;
436 		if (error)
437 			break;
438 		/* OK, start the reader thread... */
439 		error = smb_iod_create(vcp);
440 		if (error)
441 			break;
442 		vcp->vc_state = SMBIOD_ST_VCACTIVE;
443 		/* FALLTHROUGH */
444 
445 	case SMBIOD_ST_VCACTIVE:
446 		/* We can (re)use this VC. */
447 		error = 0;
448 		break;
449 
450 	default:
451 		error = EINVAL;
452 		break;
453 	}
454 
455 	SMB_VC_LOCK(vcp);
456 	cv_broadcast(&vcp->vc_statechg);
457 	SMB_VC_UNLOCK(vcp);
458 
459 	return (error);
460 }
461 
462 int
463 smb_sm_tcon(
464 	struct smb_sharespec *shspec,
465 	struct smb_cred *scred,
466 	struct smb_vc *vcp,
467 	struct smb_share **sspp)
468 {
469 	struct smb_share *ssp;
470 	int error;
471 
472 	*sspp = ssp = NULL;
473 
474 	if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
475 		/*
476 		 * The wait for vc_state in smb_sm_negotiate
477 		 * _should_ get us a VC in the right state.
478 		 */
479 		SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state);
480 		return (ENOTCONN);
481 	}
482 
483 	SMB_VC_LOCK(vcp);
484 	error = smb_vc_lookupshare(vcp, shspec, scred, &ssp);
485 	if (error) {
486 		/* The share was not found.  Create? */
487 		if ((shspec->optflags & SMBVOPT_CREATE) == 0) {
488 			SMB_VC_UNLOCK(vcp);
489 			return (error);
490 		}
491 		error = smb_share_create(vcp, shspec, scred, &ssp);
492 		if (error) {
493 			/* Could not create? Unusual. */
494 			SMB_VC_UNLOCK(vcp);
495 			return (error);
496 		}
497 		/* Note: co_usecount == 1 */
498 	}
499 	SMB_VC_UNLOCK(vcp);
500 
501 	/*
502 	 * We have a share, held, but not locked.
503 	 * Make it connected...
504 	 */
505 	SMB_SS_LOCK(ssp);
506 	if (!smb_share_valid(ssp))
507 		error = smb_share_tcon(ssp);
508 	SMB_SS_UNLOCK(ssp);
509 
510 	if (error) {
511 		/*
512 		 * Undo hold from lookupshare,
513 		 * or destroy if from _create.
514 		 */
515 		smb_share_rele(ssp);
516 	} else {
517 		/* Return it held. */
518 		*sspp = ssp;
519 	}
520 
521 	return (error);
522 }
523 
524 /*
525  * Common code for connection object
526  */
527 /*ARGSUSED*/
528 void
529 smb_co_init(struct smb_connobj *cp, int level, char *objname)
530 {
531 
532 	mutex_init(&cp->co_lock, objname,  MUTEX_DRIVER, NULL);
533 
534 	cp->co_level = level;
535 	cp->co_usecount = 1;
536 	SLIST_INIT(&cp->co_children);
537 }
538 
539 /*
540  * Called just before free of an object
541  * of which smb_connobj is a part, i.e.
542  * _vc_free, _share_free, also sm_done.
543  */
544 void
545 smb_co_done(struct smb_connobj *cp)
546 {
547 	ASSERT(SLIST_EMPTY(&cp->co_children));
548 	mutex_destroy(&cp->co_lock);
549 }
550 
551 static void
552 smb_co_addchild(
553 	struct smb_connobj *parent,
554 	struct smb_connobj *child)
555 {
556 
557 	/*
558 	 * Set the child's pointer to the parent.
559 	 * No references yet, so no need to lock.
560 	 */
561 	ASSERT(child->co_usecount == 1);
562 	child->co_parent = parent;
563 
564 	/*
565 	 * Add the child to the parent's list of
566 	 * children, and in-line smb_co_hold
567 	 */
568 	ASSERT(MUTEX_HELD(&parent->co_lock));
569 	parent->co_usecount++;
570 	SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
571 }
572 
573 void
574 smb_co_hold(struct smb_connobj *cp)
575 {
576 	SMB_CO_LOCK(cp);
577 	cp->co_usecount++;
578 	SMB_CO_UNLOCK(cp);
579 }
580 
581 /*
582  * Called via smb_vc_rele, smb_share_rele
583  */
584 void
585 smb_co_rele(struct smb_connobj *co)
586 {
587 	struct smb_connobj *parent;
588 	int old_flags;
589 
590 	SMB_CO_LOCK(co);
591 	if (co->co_usecount > 1) {
592 		co->co_usecount--;
593 		SMB_CO_UNLOCK(co);
594 		return;
595 	}
596 	ASSERT(co->co_usecount == 1);
597 	co->co_usecount = 0;
598 
599 	/*
600 	 * This list of children should be empty now.
601 	 * Check this while we're still linked, so
602 	 * we have a better chance of debugging.
603 	 */
604 	ASSERT(SLIST_EMPTY(&co->co_children));
605 
606 	/*
607 	 * OK, this element is going away.
608 	 *
609 	 * We need to drop the lock on this CO so we can take the
610 	 * parent CO lock. The _GONE flag prevents this CO from
611 	 * getting new references before we can unlink it from the
612 	 * parent list.
613 	 *
614 	 * The _GONE flag is also used to ensure that the co_gone
615 	 * function is called only once.  Note that smb_co_kill may
616 	 * do this before we get here.  If we find that the _GONE
617 	 * flag was not already set, then call the co_gone hook
618 	 * (smb_share_gone, smb_vc_gone) which will disconnect
619 	 * the share or the VC, respectively.
620 	 *
621 	 * Note the old: smb_co_gone(co, scred);
622 	 * is now in-line here.
623 	 */
624 	old_flags = co->co_flags;
625 	co->co_flags |= SMBO_GONE;
626 	SMB_CO_UNLOCK(co);
627 
628 	if ((old_flags & SMBO_GONE) == 0 && co->co_gone)
629 		co->co_gone(co);
630 
631 	/*
632 	 * If we have a parent (only smb_vclist does not)
633 	 * then unlink from parent's list of children.
634 	 * We have the only reference to the child.
635 	 */
636 	parent = co->co_parent;
637 	if (parent) {
638 		SMB_CO_LOCK(parent);
639 		ASSERT(SLIST_FIRST(&parent->co_children));
640 		if (SLIST_FIRST(&parent->co_children)) {
641 			SLIST_REMOVE(&parent->co_children, co,
642 			    smb_connobj, co_next);
643 		}
644 		SMB_CO_UNLOCK(parent);
645 	}
646 
647 	/*
648 	 * Now it's safe to free the CO
649 	 */
650 	if (co->co_free) {
651 		co->co_free(co);
652 	}
653 
654 	/*
655 	 * Finally, if the CO had a parent, decrement
656 	 * the parent's hold count for the lost child.
657 	 */
658 	if (parent) {
659 		/*
660 		 * Recursive call here (easier for debugging).
661 		 * Can only go two levels.
662 		 */
663 		smb_co_rele(parent);
664 	}
665 }
666 
667 /*
668  * Do just the first part of what co_gone does,
669  * i.e. tree disconnect, or disconnect a VC.
670  * This is used to forcibly close things.
671  */
672 void
673 smb_co_kill(struct smb_connobj *co)
674 {
675 	int old_flags;
676 
677 	SMB_CO_LOCK(co);
678 	old_flags = co->co_flags;
679 	co->co_flags |= SMBO_GONE;
680 	SMB_CO_UNLOCK(co);
681 
682 	/*
683 	 * Do the same "call only once" logic here as in
684 	 * smb_co_rele, though it's probably not possible
685 	 * for this to be called after smb_co_rele.
686 	 */
687 	if ((old_flags & SMBO_GONE) == 0 && co->co_gone)
688 		co->co_gone(co);
689 
690 	/* XXX: Walk list of children and kill those too? */
691 }
692 
693 
694 /*
695  * Session implementation
696  */
697 
698 /*
699  * This sets the fields that are allowed to change
700  * when doing a reconnect.  Many others are set in
701  * smb_vc_create and never change afterwards.
702  * Don't want domain or user to change here.
703  */
704 int
705 smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred,
706 	struct smb_vc *vcp, int is_ss)
707 {
708 	int error, minauth;
709 
710 	/* Just save all the SMBVOPT_ options. */
711 	vcp->vc_vopt = vcspec->optflags;
712 
713 	/* Cleared if nego response shows antique server! */
714 	vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
715 
716 	/* XXX: Odd place for this. */
717 	if (vcspec->optflags & SMBVOPT_EXT_SEC)
718 		vcp->vc_hflags2 |= SMB_FLAGS2_EXT_SEC;
719 
720 	if (is_ss) {
721 		/* Called from smb_sm_ssnsetup */
722 
723 		if (vcspec->optflags & SMBVOPT_USE_KEYCHAIN) {
724 			/*
725 			 * Get p/w hashes from the keychain.
726 			 * The password in vcspec->pass is
727 			 * fiction, so don't store it.
728 			 */
729 			error = smb_pkey_getpwh(vcp, scred->vc_ucred);
730 			return (error);
731 		}
732 
733 		/*
734 		 * Note: this can be called more than once
735 		 * for a given vcp, so free the old strings.
736 		 */
737 		SMB_STRFREE(vcp->vc_pass);
738 
739 		/*
740 		 * Don't store the cleartext password
741 		 * unless the minauth value was changed
742 		 * to allow use of cleartext passwords.
743 		 * (By default, this is not allowed.)
744 		 */
745 		minauth = vcspec->optflags & SMBVOPT_MINAUTH;
746 		if (minauth == SMBVOPT_MINAUTH_NONE)
747 			vcp->vc_pass = smb_strdup(vcspec->pass);
748 
749 		/* Compute LM and NTLM hashes. */
750 		smb_oldlm_hash(vcspec->pass, vcp->vc_lmhash);
751 		smb_ntlmv1hash(vcspec->pass, vcp->vc_nthash);
752 	}
753 
754 	/* Success! */
755 	error = 0;
756 	return (error);
757 }
758 
759 /*ARGSUSED*/
760 int
761 smb_vc_create(struct smb_vcspec *vcspec,
762 	struct smb_cred *scred, struct smb_vc **vcpp)
763 {
764 	static char objtype[] = "smb_vc";
765 	struct smb_vc *vcp;
766 	int error = 0;
767 
768 	ASSERT(MUTEX_HELD(&smb_vclist.co_lock));
769 
770 	/*
771 	 * Checks for valid uid/gid are now in
772 	 * smb_usr_ioc2vcspec, so at this point
773 	 * we know the user has right to create
774 	 * with the uid/gid in the vcspec.
775 	 */
776 
777 	vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP);
778 
779 	smb_co_init(VCTOCP(vcp), SMBL_VC, objtype);
780 	vcp->vc_co.co_free = smb_vc_free;
781 	vcp->vc_co.co_gone = smb_vc_gone;
782 
783 	cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL);
784 	sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL);
785 	rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL);
786 	cv_init(&vcp->iod_exit, objtype, CV_DRIVER, NULL);
787 
788 	vcp->vc_number = atomic_inc_uint_nv(&smb_vcnext);
789 	vcp->vc_state = SMBIOD_ST_NOTCONN;
790 	vcp->vc_timo = SMB_DEFRQTIMO;
791 	/*
792 	 * I think SMB_UID_UNKNOWN is not the correct
793 	 * initial value for vc_smbuid. See the long
794 	 * comment in smb_iod_sendrq()
795 	 */
796 	vcp->vc_smbuid = SMB_UID_UNKNOWN; /* XXX should be zero */
797 	vcp->vc_tdesc = &smb_tran_nbtcp_desc;
798 
799 	/*
800 	 * These identify the connection.
801 	 */
802 	vcp->vc_zoneid = getzoneid();
803 	vcp->vc_uid = vcspec->owner;
804 	vcp->vc_grp = vcspec->group;
805 	vcp->vc_mode = vcspec->rights & SMBM_MASK;
806 
807 	vcp->vc_domain = smb_strdup(vcspec->domain);
808 	vcp->vc_username = smb_strdup(vcspec->username);
809 	vcp->vc_srvname = smb_strdup(vcspec->srvname);
810 	vcp->vc_paddr = smb_dup_sockaddr(vcspec->sap);
811 	vcp->vc_laddr = smb_dup_sockaddr(vcspec->lap);
812 
813 #ifdef NOICONVSUPPORT
814 	/*
815 	 * REVISIT
816 	 */
817 	error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower);
818 	if (error)
819 		goto errout;
820 
821 	error = iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper);
822 	if (error)
823 		goto errout;
824 
825 	if (vcspec->servercs[0]) {
826 
827 		error = iconv_open(vcspec->servercs, vcspec->localcs,
828 		    &vcp->vc_toserver);
829 		if (error)
830 			goto errout;
831 
832 		error = iconv_open(vcspec->localcs, vcspec->servercs,
833 		    &vcp->vc_tolocal);
834 		if (error)
835 			goto errout;
836 	}
837 #endif /* NOICONVSUPPORT */
838 
839 	/* This fills in vcp->vc_tdata */
840 	if ((error = SMB_TRAN_CREATE(vcp, curproc)) != 0)
841 		goto errout;
842 
843 	/* Success! */
844 	smb_co_addchild(&smb_vclist, VCTOCP(vcp));
845 	*vcpp = vcp;
846 	return (0);
847 
848 errout:
849 	/*
850 	 * This will destroy the new vc.
851 	 * See: smb_vc_free
852 	 */
853 	smb_vc_rele(vcp);
854 	return (error);
855 }
856 
857 void
858 smb_vc_hold(struct smb_vc *vcp)
859 {
860 	smb_co_hold(VCTOCP(vcp));
861 }
862 
863 void
864 smb_vc_rele(struct smb_vc *vcp)
865 {
866 	smb_co_rele(VCTOCP(vcp));
867 }
868 
869 void
870 smb_vc_kill(struct smb_vc *vcp)
871 {
872 	smb_co_kill(VCTOCP(vcp));
873 }
874 
875 /*
876  * Normally called via smb_vc_rele()
877  * after co_usecount drops to zero.
878  * Also called via: smb_vc_kill()
879  *
880  * Shutdown the VC to this server,
881  * invalidate shares linked with it.
882  */
883 /*ARGSUSED*/
884 static void
885 smb_vc_gone(struct smb_connobj *cp)
886 {
887 	struct smb_vc *vcp = CPTOVC(cp);
888 
889 	/*
890 	 * Was smb_vc_disconnect(vcp);
891 	 */
892 	smb_iod_disconnect(vcp);
893 
894 	/* Note: smb_iod_destroy in vc_free */
895 }
896 
897 static void
898 smb_vc_free(struct smb_connobj *cp)
899 {
900 	struct smb_vc *vcp = CPTOVC(cp);
901 
902 	/*
903 	 * The VC has no more references, so
904 	 * no locks should be needed here.
905 	 * Make sure the IOD is gone.
906 	 */
907 	smb_iod_destroy(vcp);
908 
909 	if (vcp->vc_tdata)
910 		SMB_TRAN_DONE(vcp, curproc);
911 
912 	SMB_STRFREE(vcp->vc_username);
913 	SMB_STRFREE(vcp->vc_srvname);
914 	SMB_STRFREE(vcp->vc_pass);
915 	SMB_STRFREE(vcp->vc_domain);
916 	if (vcp->vc_paddr) {
917 		smb_free_sockaddr(vcp->vc_paddr);
918 		vcp->vc_paddr = NULL;
919 	}
920 	if (vcp->vc_laddr) {
921 		smb_free_sockaddr(vcp->vc_laddr);
922 		vcp->vc_laddr = NULL;
923 	}
924 
925 /*
926  * We are not using the iconv routines here. So commenting them for now.
927  * REVISIT.
928  */
929 #ifdef NOTYETDEFINED
930 	if (vcp->vc_tolower)
931 		iconv_close(vcp->vc_tolower);
932 	if (vcp->vc_toupper)
933 		iconv_close(vcp->vc_toupper);
934 	if (vcp->vc_tolocal)
935 		iconv_close(vcp->vc_tolocal);
936 	if (vcp->vc_toserver)
937 		iconv_close(vcp->vc_toserver);
938 #endif
939 	if (vcp->vc_intok)
940 		kmem_free(vcp->vc_intok, vcp->vc_intoklen);
941 	if (vcp->vc_outtok)
942 		kmem_free(vcp->vc_outtok, vcp->vc_outtoklen);
943 	if (vcp->vc_negtok)
944 		kmem_free(vcp->vc_negtok, vcp->vc_negtoklen);
945 
946 	cv_destroy(&vcp->iod_exit);
947 	rw_destroy(&vcp->iod_rqlock);
948 	sema_destroy(&vcp->vc_sendlock);
949 	cv_destroy(&vcp->vc_statechg);
950 	smb_co_done(VCTOCP(vcp));
951 	kmem_free(vcp, sizeof (*vcp));
952 }
953 
954 
955 /*
956  * Lookup share in the given VC. Share referenced and locked on return.
957  * VC expected to be locked on entry and will be left locked on exit.
958  */
959 /*ARGSUSED*/
960 int
961 smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec,
962 	struct smb_cred *scred,	struct smb_share **sspp)
963 {
964 	struct smb_connobj *co;
965 	struct smb_share *ssp = NULL;
966 
967 	ASSERT(MUTEX_HELD(&vcp->vc_lock));
968 
969 	*sspp = NULL;
970 
971 	/* var, head, next_field */
972 	SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
973 		ssp = CPTOSS(co);
974 
975 		/* No new refs if _GONE is set. */
976 		if (ssp->ss_flags & SMBS_GONE)
977 			continue;
978 
979 		/* This has a hold, so no need to lock it. */
980 		if (strcmp(ssp->ss_name, shspec->name) == 0)
981 			goto found;
982 	}
983 	return (ENOENT);
984 
985 found:
986 	/* Return it with a hold. */
987 	smb_share_hold(ssp);
988 	*sspp = ssp;
989 	return (0);
990 }
991 
992 
993 static char smb_emptypass[] = "";
994 
995 const char *
996 smb_vc_getpass(struct smb_vc *vcp)
997 {
998 	if (vcp->vc_pass)
999 		return (vcp->vc_pass);
1000 	return (smb_emptypass);
1001 }
1002 
1003 uint16_t
1004 smb_vc_nextmid(struct smb_vc *vcp)
1005 {
1006 	uint16_t r;
1007 
1008 	r = atomic_inc_16_nv(&vcp->vc_mid);
1009 	return (r);
1010 }
1011 
1012 /*
1013  * Get a pointer to the IP address suitable for passing to Trusted
1014  * Extensions find_tpc() routine.  Used by smbfs_mount_label_policy().
1015  * Compare this code to nfs_mount_label_policy() if problems arise.
1016  * Without support for direct CIFS-over-TCP, we should always see
1017  * an AF_NETBIOS sockaddr here.
1018  */
1019 void *
1020 smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers)
1021 {
1022 	switch (vcp->vc_paddr->sa_family) {
1023 	case AF_NETBIOS: {
1024 		struct sockaddr_nb *snb;
1025 
1026 		*ipvers = IPV4_VERSION;
1027 		/*LINTED*/
1028 		snb = (struct sockaddr_nb *)vcp->vc_paddr;
1029 		return ((void *)&snb->snb_ipaddr);
1030 	}
1031 	case AF_INET: {
1032 		struct sockaddr_in *sin;
1033 
1034 		*ipvers = IPV4_VERSION;
1035 		/*LINTED*/
1036 		sin = (struct sockaddr_in *)vcp->vc_paddr;
1037 		return ((void *)&sin->sin_addr);
1038 	}
1039 	case AF_INET6: {
1040 		struct sockaddr_in6 *sin6;
1041 
1042 		*ipvers = IPV6_VERSION;
1043 		/*LINTED*/
1044 		sin6 = (struct sockaddr_in6 *)vcp->vc_paddr;
1045 		return ((void *)&sin6->sin6_addr);
1046 	}
1047 	default:
1048 		SMBSDEBUG("invalid address family %d\n",
1049 		    vcp->vc_paddr->sa_family);
1050 		*ipvers = 0;
1051 		return (NULL);
1052 	}
1053 }
1054 
1055 /*
1056  * Share implementation
1057  */
1058 /*
1059  * Allocate share structure and attach it to the given VC
1060  * Connection expected to be locked on entry. Share will be returned
1061  * in locked state.
1062  */
1063 /*ARGSUSED*/
1064 int
1065 smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
1066 	struct smb_cred *scred, struct smb_share **sspp)
1067 {
1068 	static char objtype[] = "smb_ss";
1069 	struct smb_share *ssp;
1070 
1071 	ASSERT(MUTEX_HELD(&vcp->vc_lock));
1072 
1073 	ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP);
1074 	smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype);
1075 	ssp->ss_co.co_free = smb_share_free;
1076 	ssp->ss_co.co_gone = smb_share_gone;
1077 
1078 	ssp->ss_name = smb_strdup(shspec->name);
1079 	ssp->ss_mount = NULL;
1080 	if (shspec->pass && shspec->pass[0])
1081 		ssp->ss_pass = smb_strdup(shspec->pass);
1082 	ssp->ss_type = shspec->stype;
1083 	ssp->ss_tid = SMB_TID_UNKNOWN;
1084 	ssp->ss_mode = shspec->rights & SMBM_MASK;
1085 	ssp->ss_fsname = NULL;
1086 	smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
1087 	*sspp = ssp;
1088 
1089 	return (0);
1090 }
1091 
1092 /*
1093  * Normally called via smb_share_rele()
1094  * after co_usecount drops to zero.
1095  */
1096 static void
1097 smb_share_free(struct smb_connobj *cp)
1098 {
1099 	struct smb_share *ssp = CPTOSS(cp);
1100 
1101 	SMB_STRFREE(ssp->ss_name);
1102 	SMB_STRFREE(ssp->ss_pass);
1103 	SMB_STRFREE(ssp->ss_fsname);
1104 	smb_co_done(SSTOCP(ssp));
1105 	kmem_free(ssp, sizeof (*ssp));
1106 }
1107 
1108 /*
1109  * Normally called via smb_share_rele()
1110  * after co_usecount drops to zero.
1111  * Also called via: smb_share_kill()
1112  */
1113 static void
1114 smb_share_gone(struct smb_connobj *cp)
1115 {
1116 	struct smb_cred scred;
1117 	struct smb_share *ssp = CPTOSS(cp);
1118 
1119 	smb_credinit(&scred, curproc, NULL);
1120 	smb_iod_shutdown_share(ssp);
1121 	smb_smb_treedisconnect(ssp, &scred);
1122 	smb_credrele(&scred);
1123 }
1124 
1125 void
1126 smb_share_hold(struct smb_share *ssp)
1127 {
1128 	smb_co_hold(SSTOCP(ssp));
1129 }
1130 
1131 void
1132 smb_share_rele(struct smb_share *ssp)
1133 {
1134 	smb_co_rele(SSTOCP(ssp));
1135 }
1136 
1137 void
1138 smb_share_kill(struct smb_share *ssp)
1139 {
1140 	smb_co_kill(SSTOCP(ssp));
1141 }
1142 
1143 
1144 void
1145 smb_share_invalidate(struct smb_share *ssp)
1146 {
1147 	ssp->ss_tid = SMB_TID_UNKNOWN;
1148 }
1149 
1150 /*
1151  * Returns NON-zero if the share is valid.
1152  * Called with the share locked.
1153  */
1154 int
1155 smb_share_valid(struct smb_share *ssp)
1156 {
1157 	struct smb_vc *vcp = SSTOVC(ssp);
1158 
1159 	ASSERT(MUTEX_HELD(&ssp->ss_lock));
1160 
1161 	if ((ssp->ss_flags & SMBS_CONNECTED) == 0)
1162 		return (0);
1163 
1164 	if (ssp->ss_tid == SMB_TID_UNKNOWN) {
1165 		SMBIODEBUG("found TID unknown\n");
1166 		ssp->ss_flags &= ~SMBS_CONNECTED;
1167 	}
1168 
1169 	if (ssp->ss_vcgenid != vcp->vc_genid) {
1170 		SMBIODEBUG("wrong genid\n");
1171 		ssp->ss_flags &= ~SMBS_CONNECTED;
1172 	}
1173 
1174 	return (ssp->ss_flags & SMBS_CONNECTED);
1175 }
1176 
1177 /*
1178  * Connect (or reconnect) a share object.
1179  * Called with the share locked.
1180  */
1181 int
1182 smb_share_tcon(struct smb_share *ssp)
1183 {
1184 	struct smb_vc *vcp = SSTOVC(ssp);
1185 	clock_t tmo;
1186 	int error;
1187 
1188 	ASSERT(MUTEX_HELD(&ssp->ss_lock));
1189 
1190 	if (ssp->ss_flags & SMBS_CONNECTED) {
1191 		SMBIODEBUG("alread connected?");
1192 		return (0);
1193 	}
1194 
1195 	/*
1196 	 * Wait for completion of any state changes
1197 	 * that might be underway.
1198 	 */
1199 	while (ssp->ss_flags & SMBS_RECONNECTING) {
1200 		ssp->ss_conn_waiters++;
1201 		tmo = cv_wait_sig(&ssp->ss_conn_done, &ssp->ss_lock);
1202 		ssp->ss_conn_waiters--;
1203 		if (tmo == 0) {
1204 			/* Interrupt! */
1205 			return (EINTR);
1206 		}
1207 	}
1208 
1209 	/* Did someone else do it for us? */
1210 	if (ssp->ss_flags & SMBS_CONNECTED)
1211 		return (0);
1212 
1213 	/*
1214 	 * OK, we'll do the work.
1215 	 */
1216 	ssp->ss_flags |= SMBS_RECONNECTING;
1217 
1218 	/* Drop the lock while doing the call. */
1219 	SMB_SS_UNLOCK(ssp);
1220 	error = smb_smb_treeconnect(ssp, &vcp->vc_scred);
1221 	SMB_SS_LOCK(ssp);
1222 
1223 	if (!error)
1224 		ssp->ss_flags |= SMBS_CONNECTED;
1225 	ssp->ss_flags &= ~SMBS_RECONNECTING;
1226 
1227 	/* They can all go ahead! */
1228 	if (ssp->ss_conn_waiters)
1229 		cv_broadcast(&ssp->ss_conn_done);
1230 
1231 	return (error);
1232 }
1233 
1234 const char *
1235 smb_share_getpass(struct smb_share *ssp)
1236 {
1237 	struct smb_vc *vcp;
1238 
1239 	if (ssp->ss_pass)
1240 		return (ssp->ss_pass);
1241 	vcp = SSTOVC(ssp);
1242 	if (vcp->vc_pass)
1243 		return (vcp->vc_pass);
1244 	return (smb_emptypass);
1245 }
1246 
1247 int
1248 smb_share_count(void)
1249 {
1250 	struct smb_connobj *covc, *coss;
1251 	struct smb_vc *vcp;
1252 	zoneid_t zoneid = getzoneid();
1253 	int nshares = 0;
1254 
1255 	SMB_CO_LOCK(&smb_vclist);
1256 	SLIST_FOREACH(covc, &smb_vclist.co_children, co_next) {
1257 		vcp = CPTOVC(covc);
1258 
1259 		/* VCs in other zones are invisibile. */
1260 		if (vcp->vc_zoneid != zoneid)
1261 			continue;
1262 
1263 		SMB_VC_LOCK(vcp);
1264 
1265 		/* var, head, next_field */
1266 		SLIST_FOREACH(coss, &(VCTOCP(vcp)->co_children), co_next) {
1267 			nshares++;
1268 		}
1269 
1270 		SMB_VC_UNLOCK(vcp);
1271 	}
1272 	SMB_CO_UNLOCK(&smb_vclist);
1273 
1274 	return (nshares);
1275 }
1276 
1277 /*
1278  * Solaris zones support
1279  */
1280 /*ARGSUSED*/
1281 void
1282 lingering_vc(struct smb_vc *vc)
1283 {
1284 	/* good place for a breakpoint */
1285 	DEBUG_ENTER("lingering VC");
1286 }
1287 
1288 /*
1289  * On zone shutdown, kill any IOD threads still running in this zone.
1290  */
1291 /* ARGSUSED */
1292 void
1293 nsmb_zone_shutdown(zoneid_t zoneid, void *data)
1294 {
1295 	struct smb_connobj *co;
1296 	struct smb_vc *vcp;
1297 
1298 	SMB_CO_LOCK(&smb_vclist);
1299 	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
1300 		vcp = CPTOVC(co);
1301 
1302 		if (vcp->vc_zoneid != zoneid)
1303 			continue;
1304 
1305 		/*
1306 		 * This will close the connection, and
1307 		 * cause the IOD thread to terminate.
1308 		 */
1309 		smb_vc_kill(vcp);
1310 	}
1311 	SMB_CO_UNLOCK(&smb_vclist);
1312 }
1313 
1314 /*
1315  * On zone destroy, kill any IOD threads and free all resources they used.
1316  */
1317 /* ARGSUSED */
1318 void
1319 nsmb_zone_destroy(zoneid_t zoneid, void *data)
1320 {
1321 	struct smb_connobj *co;
1322 	struct smb_vc *vcp;
1323 
1324 	/*
1325 	 * We will repeat what should have already happened
1326 	 * in zone_shutdown to make things go away.
1327 	 *
1328 	 * There should have been an smb_vc_rele call
1329 	 * by now for all VCs in the zone.  If not,
1330 	 * there's probably more we needed to do in
1331 	 * the shutdown call.
1332 	 */
1333 
1334 	SMB_CO_LOCK(&smb_vclist);
1335 
1336 	if (smb_vclist.co_usecount > 1) {
1337 		SMBERROR("%d connections still active\n",
1338 		    smb_vclist.co_usecount - 1);
1339 	}
1340 
1341 	/* var, head, next_field */
1342 	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
1343 		vcp = CPTOVC(co);
1344 
1345 		if (vcp->vc_zoneid != zoneid)
1346 			continue;
1347 
1348 		/* Debugging */
1349 		lingering_vc(vcp);
1350 	}
1351 
1352 	SMB_CO_UNLOCK(&smb_vclist);
1353 }
1354