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