xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c (revision 57841ad7c4da76238b3e15b3f642e30e046a5256)
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 	if (is_ss) {
714 		/* Called from smb_sm_ssnsetup */
715 
716 		if (vcspec->optflags & SMBVOPT_USE_KEYCHAIN) {
717 			/*
718 			 * Get p/w hashes from the keychain.
719 			 * The password in vcspec->pass is
720 			 * fiction, so don't store it.
721 			 */
722 			error = smb_pkey_getpwh(vcp, scred->vc_ucred);
723 			return (error);
724 		}
725 
726 		/*
727 		 * Note: this can be called more than once
728 		 * for a given vcp, so free the old strings.
729 		 */
730 		SMB_STRFREE(vcp->vc_pass);
731 
732 		/*
733 		 * Don't store the cleartext password
734 		 * unless the minauth value was changed
735 		 * to allow use of cleartext passwords.
736 		 * (By default, this is not allowed.)
737 		 */
738 		minauth = vcspec->optflags & SMBVOPT_MINAUTH;
739 		if (minauth == SMBVOPT_MINAUTH_NONE)
740 			vcp->vc_pass = smb_strdup(vcspec->pass);
741 
742 		/* Compute LM and NTLM hashes. */
743 		smb_oldlm_hash(vcspec->pass, vcp->vc_lmhash);
744 		smb_ntlmv1hash(vcspec->pass, vcp->vc_nthash);
745 	}
746 
747 	/* Success! */
748 	error = 0;
749 	return (error);
750 }
751 
752 /*ARGSUSED*/
753 int
754 smb_vc_create(struct smb_vcspec *vcspec,
755 	struct smb_cred *scred, struct smb_vc **vcpp)
756 {
757 	static char objtype[] = "smb_vc";
758 	struct smb_vc *vcp;
759 	int error = 0;
760 
761 	ASSERT(MUTEX_HELD(&smb_vclist.co_lock));
762 
763 	/*
764 	 * Checks for valid uid/gid are now in
765 	 * smb_usr_ioc2vcspec, so at this point
766 	 * we know the user has right to create
767 	 * with the uid/gid in the vcspec.
768 	 */
769 
770 	vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP);
771 
772 	smb_co_init(VCTOCP(vcp), SMBL_VC, objtype);
773 	vcp->vc_co.co_free = smb_vc_free;
774 	vcp->vc_co.co_gone = smb_vc_gone;
775 
776 	cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL);
777 	sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL);
778 	rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL);
779 	cv_init(&vcp->iod_exit, objtype, CV_DRIVER, NULL);
780 
781 	vcp->vc_number = atomic_inc_uint_nv(&smb_vcnext);
782 	vcp->vc_state = SMBIOD_ST_NOTCONN;
783 	vcp->vc_timo = SMB_DEFRQTIMO;
784 	/*
785 	 * I think SMB_UID_UNKNOWN is not the correct
786 	 * initial value for vc_smbuid. See the long
787 	 * comment in smb_iod_sendrq()
788 	 */
789 	vcp->vc_smbuid = SMB_UID_UNKNOWN; /* XXX should be zero */
790 	vcp->vc_tdesc = &smb_tran_nbtcp_desc;
791 
792 	/*
793 	 * These identify the connection.
794 	 */
795 	vcp->vc_zoneid = getzoneid();
796 	vcp->vc_uid = vcspec->owner;
797 	vcp->vc_grp = vcspec->group;
798 	vcp->vc_mode = vcspec->rights & SMBM_MASK;
799 
800 	vcp->vc_domain = smb_strdup(vcspec->domain);
801 	vcp->vc_username = smb_strdup(vcspec->username);
802 	vcp->vc_srvname = smb_strdup(vcspec->srvname);
803 	vcp->vc_paddr = smb_dup_sockaddr(vcspec->sap);
804 	vcp->vc_laddr = smb_dup_sockaddr(vcspec->lap);
805 
806 #ifdef NOICONVSUPPORT
807 	/*
808 	 * REVISIT
809 	 */
810 	error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower);
811 	if (error)
812 		goto errout;
813 
814 	error = iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper);
815 	if (error)
816 		goto errout;
817 
818 	if (vcspec->servercs[0]) {
819 
820 		error = iconv_open(vcspec->servercs, vcspec->localcs,
821 		    &vcp->vc_toserver);
822 		if (error)
823 			goto errout;
824 
825 		error = iconv_open(vcspec->localcs, vcspec->servercs,
826 		    &vcp->vc_tolocal);
827 		if (error)
828 			goto errout;
829 	}
830 #endif /* NOICONVSUPPORT */
831 
832 	/* This fills in vcp->vc_tdata */
833 	if ((error = SMB_TRAN_CREATE(vcp, curproc)) != 0)
834 		goto errout;
835 
836 	/* Success! */
837 	smb_co_addchild(&smb_vclist, VCTOCP(vcp));
838 	*vcpp = vcp;
839 	return (0);
840 
841 errout:
842 	/*
843 	 * This will destroy the new vc.
844 	 * See: smb_vc_free
845 	 */
846 	smb_vc_rele(vcp);
847 	return (error);
848 }
849 
850 void
851 smb_vc_hold(struct smb_vc *vcp)
852 {
853 	smb_co_hold(VCTOCP(vcp));
854 }
855 
856 void
857 smb_vc_rele(struct smb_vc *vcp)
858 {
859 	smb_co_rele(VCTOCP(vcp));
860 }
861 
862 void
863 smb_vc_kill(struct smb_vc *vcp)
864 {
865 	smb_co_kill(VCTOCP(vcp));
866 }
867 
868 /*
869  * Normally called via smb_vc_rele()
870  * after co_usecount drops to zero.
871  * Also called via: smb_vc_kill()
872  *
873  * Shutdown the VC to this server,
874  * invalidate shares linked with it.
875  */
876 /*ARGSUSED*/
877 static void
878 smb_vc_gone(struct smb_connobj *cp)
879 {
880 	struct smb_vc *vcp = CPTOVC(cp);
881 
882 	/*
883 	 * Was smb_vc_disconnect(vcp);
884 	 */
885 	smb_iod_disconnect(vcp);
886 
887 	/* Note: smb_iod_destroy in vc_free */
888 }
889 
890 static void
891 smb_vc_free(struct smb_connobj *cp)
892 {
893 	struct smb_vc *vcp = CPTOVC(cp);
894 
895 	/*
896 	 * The VC has no more references, so
897 	 * no locks should be needed here.
898 	 * Make sure the IOD is gone.
899 	 */
900 	smb_iod_destroy(vcp);
901 
902 	if (vcp->vc_tdata)
903 		SMB_TRAN_DONE(vcp, curproc);
904 
905 	SMB_STRFREE(vcp->vc_username);
906 	SMB_STRFREE(vcp->vc_srvname);
907 	SMB_STRFREE(vcp->vc_pass);
908 	SMB_STRFREE(vcp->vc_domain);
909 	if (vcp->vc_paddr) {
910 		smb_free_sockaddr(vcp->vc_paddr);
911 		vcp->vc_paddr = NULL;
912 	}
913 	if (vcp->vc_laddr) {
914 		smb_free_sockaddr(vcp->vc_laddr);
915 		vcp->vc_laddr = NULL;
916 	}
917 
918 /*
919  * We are not using the iconv routines here. So commenting them for now.
920  * REVISIT.
921  */
922 #ifdef NOTYETDEFINED
923 	if (vcp->vc_tolower)
924 		iconv_close(vcp->vc_tolower);
925 	if (vcp->vc_toupper)
926 		iconv_close(vcp->vc_toupper);
927 	if (vcp->vc_tolocal)
928 		iconv_close(vcp->vc_tolocal);
929 	if (vcp->vc_toserver)
930 		iconv_close(vcp->vc_toserver);
931 #endif
932 	if (vcp->vc_intok)
933 		kmem_free(vcp->vc_intok, vcp->vc_intoklen);
934 	if (vcp->vc_outtok)
935 		kmem_free(vcp->vc_outtok, vcp->vc_outtoklen);
936 	if (vcp->vc_negtok)
937 		kmem_free(vcp->vc_negtok, vcp->vc_negtoklen);
938 
939 	if (vcp->vc_mackey != NULL)
940 		kmem_free(vcp->vc_mackey, vcp->vc_mackeylen);
941 
942 	cv_destroy(&vcp->iod_exit);
943 	rw_destroy(&vcp->iod_rqlock);
944 	sema_destroy(&vcp->vc_sendlock);
945 	cv_destroy(&vcp->vc_statechg);
946 	smb_co_done(VCTOCP(vcp));
947 	kmem_free(vcp, sizeof (*vcp));
948 }
949 
950 
951 /*
952  * Lookup share in the given VC. Share referenced and locked on return.
953  * VC expected to be locked on entry and will be left locked on exit.
954  */
955 /*ARGSUSED*/
956 int
957 smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec,
958 	struct smb_cred *scred,	struct smb_share **sspp)
959 {
960 	struct smb_connobj *co;
961 	struct smb_share *ssp = NULL;
962 
963 	ASSERT(MUTEX_HELD(&vcp->vc_lock));
964 
965 	*sspp = NULL;
966 
967 	/* var, head, next_field */
968 	SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) {
969 		ssp = CPTOSS(co);
970 
971 		/* No new refs if _GONE is set. */
972 		if (ssp->ss_flags & SMBS_GONE)
973 			continue;
974 
975 		/* This has a hold, so no need to lock it. */
976 		if (strcmp(ssp->ss_name, shspec->name) == 0)
977 			goto found;
978 	}
979 	return (ENOENT);
980 
981 found:
982 	/* Return it with a hold. */
983 	smb_share_hold(ssp);
984 	*sspp = ssp;
985 	return (0);
986 }
987 
988 
989 static char smb_emptypass[] = "";
990 
991 const char *
992 smb_vc_getpass(struct smb_vc *vcp)
993 {
994 	if (vcp->vc_pass)
995 		return (vcp->vc_pass);
996 	return (smb_emptypass);
997 }
998 
999 uint16_t
1000 smb_vc_nextmid(struct smb_vc *vcp)
1001 {
1002 	uint16_t r;
1003 
1004 	r = atomic_inc_16_nv(&vcp->vc_mid);
1005 	return (r);
1006 }
1007 
1008 /*
1009  * Get a pointer to the IP address suitable for passing to Trusted
1010  * Extensions find_tpc() routine.  Used by smbfs_mount_label_policy().
1011  * Compare this code to nfs_mount_label_policy() if problems arise.
1012  * Without support for direct CIFS-over-TCP, we should always see
1013  * an AF_NETBIOS sockaddr here.
1014  */
1015 void *
1016 smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers)
1017 {
1018 	switch (vcp->vc_paddr->sa_family) {
1019 	case AF_NETBIOS: {
1020 		struct sockaddr_nb *snb;
1021 
1022 		*ipvers = IPV4_VERSION;
1023 		/*LINTED*/
1024 		snb = (struct sockaddr_nb *)vcp->vc_paddr;
1025 		return ((void *)&snb->snb_ipaddr);
1026 	}
1027 	case AF_INET: {
1028 		struct sockaddr_in *sin;
1029 
1030 		*ipvers = IPV4_VERSION;
1031 		/*LINTED*/
1032 		sin = (struct sockaddr_in *)vcp->vc_paddr;
1033 		return ((void *)&sin->sin_addr);
1034 	}
1035 	case AF_INET6: {
1036 		struct sockaddr_in6 *sin6;
1037 
1038 		*ipvers = IPV6_VERSION;
1039 		/*LINTED*/
1040 		sin6 = (struct sockaddr_in6 *)vcp->vc_paddr;
1041 		return ((void *)&sin6->sin6_addr);
1042 	}
1043 	default:
1044 		SMBSDEBUG("invalid address family %d\n",
1045 		    vcp->vc_paddr->sa_family);
1046 		*ipvers = 0;
1047 		return (NULL);
1048 	}
1049 }
1050 
1051 /*
1052  * Share implementation
1053  */
1054 /*
1055  * Allocate share structure and attach it to the given VC
1056  * Connection expected to be locked on entry. Share will be returned
1057  * in locked state.
1058  */
1059 /*ARGSUSED*/
1060 int
1061 smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
1062 	struct smb_cred *scred, struct smb_share **sspp)
1063 {
1064 	static char objtype[] = "smb_ss";
1065 	struct smb_share *ssp;
1066 
1067 	ASSERT(MUTEX_HELD(&vcp->vc_lock));
1068 
1069 	ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP);
1070 	smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype);
1071 	ssp->ss_co.co_free = smb_share_free;
1072 	ssp->ss_co.co_gone = smb_share_gone;
1073 
1074 	ssp->ss_name = smb_strdup(shspec->name);
1075 	ssp->ss_mount = NULL;
1076 	if (shspec->pass && shspec->pass[0])
1077 		ssp->ss_pass = smb_strdup(shspec->pass);
1078 	ssp->ss_type = shspec->stype;
1079 	ssp->ss_tid = SMB_TID_UNKNOWN;
1080 	ssp->ss_mode = shspec->rights & SMBM_MASK;
1081 	ssp->ss_fsname = NULL;
1082 	smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
1083 	*sspp = ssp;
1084 
1085 	return (0);
1086 }
1087 
1088 /*
1089  * Normally called via smb_share_rele()
1090  * after co_usecount drops to zero.
1091  */
1092 static void
1093 smb_share_free(struct smb_connobj *cp)
1094 {
1095 	struct smb_share *ssp = CPTOSS(cp);
1096 
1097 	SMB_STRFREE(ssp->ss_name);
1098 	SMB_STRFREE(ssp->ss_pass);
1099 	SMB_STRFREE(ssp->ss_fsname);
1100 	smb_co_done(SSTOCP(ssp));
1101 	kmem_free(ssp, sizeof (*ssp));
1102 }
1103 
1104 /*
1105  * Normally called via smb_share_rele()
1106  * after co_usecount drops to zero.
1107  * Also called via: smb_share_kill()
1108  */
1109 static void
1110 smb_share_gone(struct smb_connobj *cp)
1111 {
1112 	struct smb_cred scred;
1113 	struct smb_share *ssp = CPTOSS(cp);
1114 
1115 	smb_credinit(&scred, curproc, NULL);
1116 	smb_iod_shutdown_share(ssp);
1117 	smb_smb_treedisconnect(ssp, &scred);
1118 	smb_credrele(&scred);
1119 }
1120 
1121 void
1122 smb_share_hold(struct smb_share *ssp)
1123 {
1124 	smb_co_hold(SSTOCP(ssp));
1125 }
1126 
1127 void
1128 smb_share_rele(struct smb_share *ssp)
1129 {
1130 	smb_co_rele(SSTOCP(ssp));
1131 }
1132 
1133 void
1134 smb_share_kill(struct smb_share *ssp)
1135 {
1136 	smb_co_kill(SSTOCP(ssp));
1137 }
1138 
1139 
1140 void
1141 smb_share_invalidate(struct smb_share *ssp)
1142 {
1143 	ssp->ss_tid = SMB_TID_UNKNOWN;
1144 }
1145 
1146 /*
1147  * Returns NON-zero if the share is valid.
1148  * Called with the share locked.
1149  */
1150 int
1151 smb_share_valid(struct smb_share *ssp)
1152 {
1153 	struct smb_vc *vcp = SSTOVC(ssp);
1154 
1155 	ASSERT(MUTEX_HELD(&ssp->ss_lock));
1156 
1157 	if ((ssp->ss_flags & SMBS_CONNECTED) == 0)
1158 		return (0);
1159 
1160 	if (ssp->ss_tid == SMB_TID_UNKNOWN) {
1161 		SMBIODEBUG("found TID unknown\n");
1162 		ssp->ss_flags &= ~SMBS_CONNECTED;
1163 	}
1164 
1165 	if (ssp->ss_vcgenid != vcp->vc_genid) {
1166 		SMBIODEBUG("wrong genid\n");
1167 		ssp->ss_flags &= ~SMBS_CONNECTED;
1168 	}
1169 
1170 	return (ssp->ss_flags & SMBS_CONNECTED);
1171 }
1172 
1173 /*
1174  * Connect (or reconnect) a share object.
1175  * Called with the share locked.
1176  */
1177 int
1178 smb_share_tcon(struct smb_share *ssp)
1179 {
1180 	struct smb_vc *vcp = SSTOVC(ssp);
1181 	clock_t tmo;
1182 	int error;
1183 
1184 	ASSERT(MUTEX_HELD(&ssp->ss_lock));
1185 
1186 	if (ssp->ss_flags & SMBS_CONNECTED) {
1187 		SMBIODEBUG("alread connected?");
1188 		return (0);
1189 	}
1190 
1191 	/*
1192 	 * Wait for completion of any state changes
1193 	 * that might be underway.
1194 	 */
1195 	while (ssp->ss_flags & SMBS_RECONNECTING) {
1196 		ssp->ss_conn_waiters++;
1197 		tmo = cv_wait_sig(&ssp->ss_conn_done, &ssp->ss_lock);
1198 		ssp->ss_conn_waiters--;
1199 		if (tmo == 0) {
1200 			/* Interrupt! */
1201 			return (EINTR);
1202 		}
1203 	}
1204 
1205 	/* Did someone else do it for us? */
1206 	if (ssp->ss_flags & SMBS_CONNECTED)
1207 		return (0);
1208 
1209 	/*
1210 	 * OK, we'll do the work.
1211 	 */
1212 	ssp->ss_flags |= SMBS_RECONNECTING;
1213 
1214 	/* Drop the lock while doing the call. */
1215 	SMB_SS_UNLOCK(ssp);
1216 	error = smb_smb_treeconnect(ssp, &vcp->vc_scred);
1217 	SMB_SS_LOCK(ssp);
1218 
1219 	if (!error)
1220 		ssp->ss_flags |= SMBS_CONNECTED;
1221 	ssp->ss_flags &= ~SMBS_RECONNECTING;
1222 
1223 	/* They can all go ahead! */
1224 	if (ssp->ss_conn_waiters)
1225 		cv_broadcast(&ssp->ss_conn_done);
1226 
1227 	return (error);
1228 }
1229 
1230 const char *
1231 smb_share_getpass(struct smb_share *ssp)
1232 {
1233 	struct smb_vc *vcp;
1234 
1235 	if (ssp->ss_pass)
1236 		return (ssp->ss_pass);
1237 	vcp = SSTOVC(ssp);
1238 	if (vcp->vc_pass)
1239 		return (vcp->vc_pass);
1240 	return (smb_emptypass);
1241 }
1242 
1243 int
1244 smb_share_count(void)
1245 {
1246 	struct smb_connobj *covc, *coss;
1247 	struct smb_vc *vcp;
1248 	zoneid_t zoneid = getzoneid();
1249 	int nshares = 0;
1250 
1251 	SMB_CO_LOCK(&smb_vclist);
1252 	SLIST_FOREACH(covc, &smb_vclist.co_children, co_next) {
1253 		vcp = CPTOVC(covc);
1254 
1255 		/* VCs in other zones are invisibile. */
1256 		if (vcp->vc_zoneid != zoneid)
1257 			continue;
1258 
1259 		SMB_VC_LOCK(vcp);
1260 
1261 		/* var, head, next_field */
1262 		SLIST_FOREACH(coss, &(VCTOCP(vcp)->co_children), co_next) {
1263 			nshares++;
1264 		}
1265 
1266 		SMB_VC_UNLOCK(vcp);
1267 	}
1268 	SMB_CO_UNLOCK(&smb_vclist);
1269 
1270 	return (nshares);
1271 }
1272 
1273 /*
1274  * Solaris zones support
1275  */
1276 /*ARGSUSED*/
1277 void
1278 lingering_vc(struct smb_vc *vc)
1279 {
1280 	/* good place for a breakpoint */
1281 	DEBUG_ENTER("lingering VC");
1282 }
1283 
1284 /*
1285  * On zone shutdown, kill any IOD threads still running in this zone.
1286  */
1287 /* ARGSUSED */
1288 void
1289 nsmb_zone_shutdown(zoneid_t zoneid, void *data)
1290 {
1291 	struct smb_connobj *co;
1292 	struct smb_vc *vcp;
1293 
1294 	SMB_CO_LOCK(&smb_vclist);
1295 	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
1296 		vcp = CPTOVC(co);
1297 
1298 		if (vcp->vc_zoneid != zoneid)
1299 			continue;
1300 
1301 		/*
1302 		 * This will close the connection, and
1303 		 * cause the IOD thread to terminate.
1304 		 */
1305 		smb_vc_kill(vcp);
1306 	}
1307 	SMB_CO_UNLOCK(&smb_vclist);
1308 }
1309 
1310 /*
1311  * On zone destroy, kill any IOD threads and free all resources they used.
1312  */
1313 /* ARGSUSED */
1314 void
1315 nsmb_zone_destroy(zoneid_t zoneid, void *data)
1316 {
1317 	struct smb_connobj *co;
1318 	struct smb_vc *vcp;
1319 
1320 	/*
1321 	 * We will repeat what should have already happened
1322 	 * in zone_shutdown to make things go away.
1323 	 *
1324 	 * There should have been an smb_vc_rele call
1325 	 * by now for all VCs in the zone.  If not,
1326 	 * there's probably more we needed to do in
1327 	 * the shutdown call.
1328 	 */
1329 
1330 	SMB_CO_LOCK(&smb_vclist);
1331 
1332 	if (smb_vclist.co_usecount > 1) {
1333 		SMBERROR("%d connections still active\n",
1334 		    smb_vclist.co_usecount - 1);
1335 	}
1336 
1337 	/* var, head, next_field */
1338 	SLIST_FOREACH(co, &smb_vclist.co_children, co_next) {
1339 		vcp = CPTOVC(co);
1340 
1341 		if (vcp->vc_zoneid != zoneid)
1342 			continue;
1343 
1344 		/* Debugging */
1345 		lingering_vc(vcp);
1346 	}
1347 
1348 	SMB_CO_UNLOCK(&smb_vclist);
1349 }
1350