1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include <sys/types.h>
33#include <sys/sysmacros.h>
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/file.h>
37#include <sys/vnode.h>
38#include <sys/errno.h>
39#include <sys/signal.h>
40#include <sys/cred.h>
41#include <sys/policy.h>
42#include <sys/conf.h>
43#include <sys/debug.h>
44#include <sys/proc.h>
45#include <sys/session.h>
46#include <sys/kmem.h>
47#include <sys/cmn_err.h>
48#include <sys/strsubr.h>
49#include <sys/fs/snode.h>
50
51sess_t session0 = {
52	&pid0,		/* s_sidp */
53	{0},		/* s_lock */
54	1,		/* s_ref */
55	B_FALSE,	/* s_sighuped */
56	B_FALSE,	/* s_exit */
57	0,		/* s_exit_cv */
58	0,		/* s_cnt */
59	0,		/* s_cnt_cv */
60	NODEV,		/* s_dev */
61	NULL,		/* s_vp */
62	NULL		/* s_cred */
63};
64
65void
66sess_hold(proc_t *p)
67{
68	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&p->p_splock));
69	mutex_enter(&p->p_sessp->s_lock);
70	p->p_sessp->s_ref++;
71	mutex_exit(&p->p_sessp->s_lock);
72}
73
74void
75sess_rele(sess_t *sp, boolean_t pidlock_held)
76{
77	ASSERT(MUTEX_HELD(&pidlock) || !pidlock_held);
78
79	mutex_enter(&sp->s_lock);
80
81	ASSERT(sp->s_ref != 0);
82	if (--sp->s_ref > 0) {
83		mutex_exit(&sp->s_lock);
84		return;
85	}
86	ASSERT(sp->s_ref == 0);
87
88	/*
89	 * It's ok to free this session structure now because we know
90	 * that no one else can have a pointer to it.  We know this
91	 * to be true because the only time that s_ref can possibly
92	 * be incremented is when pidlock or p_splock is held AND there
93	 * is a proc_t that points to that session structure.  In that
94	 * case we are guaranteed that the s_ref is at least 1 since there
95	 * is a proc_t that points to it.  So when s_ref finally drops to
96	 * zero then no one else has a reference (and hence pointer) to
97	 * this session structure and there is no valid proc_t pointing
98	 * to this session structure anymore so, no one can acquire a
99	 * reference (and pointer) to this session structure so it's
100	 * ok to free it here.
101	 */
102
103	if (sp == &session0)
104		panic("sp == &session0");
105
106	/* make sure there are no outstanding holds */
107	ASSERT(sp->s_cnt == 0);
108
109	/* make sure there is no exit in progress */
110	ASSERT(!sp->s_exit);
111
112	/* make sure someone already freed any ctty */
113	ASSERT(sp->s_vp == NULL);
114	ASSERT(sp->s_dev == NODEV);
115
116	if (!pidlock_held)
117		mutex_enter(&pidlock);
118	PID_RELE(sp->s_sidp);
119	if (!pidlock_held)
120		mutex_exit(&pidlock);
121
122	mutex_destroy(&sp->s_lock);
123	cv_destroy(&sp->s_cnt_cv);
124	kmem_free(sp, sizeof (sess_t));
125}
126
127sess_t *
128tty_hold(void)
129{
130	proc_t		*p = curproc;
131	sess_t		*sp;
132	boolean_t	got_sig = B_FALSE;
133
134	/* make sure the caller isn't holding locks they shouldn't */
135	ASSERT(MUTEX_NOT_HELD(&pidlock));
136
137	for (;;) {
138		mutex_enter(&p->p_splock);	/* protect p->p_sessp */
139		sp = p->p_sessp;
140		mutex_enter(&sp->s_lock);	/* protect sp->* */
141
142		/* make sure the caller isn't holding locks they shouldn't */
143		ASSERT((sp->s_vp == NULL) ||
144		    MUTEX_NOT_HELD(&sp->s_vp->v_stream->sd_lock));
145
146		/*
147		 * If the session leader process is not exiting (and hence
148		 * not trying to release the session's ctty) then we can
149		 * safely grab a hold on the current session structure
150		 * and return it.  If on the other hand the session leader
151		 * process is exiting and clearing the ctty then we'll
152		 * wait till it's done before we loop around and grab a
153		 * hold on the session structure.
154		 */
155		if (!sp->s_exit)
156			break;
157
158		/* need to hold the session so it can't be freed */
159		sp->s_ref++;
160		mutex_exit(&p->p_splock);
161
162		/* Wait till the session leader is done */
163		if (!cv_wait_sig(&sp->s_exit_cv, &sp->s_lock))
164			got_sig = B_TRUE;
165
166		/*
167		 * Now we need to drop our hold on the session structure,
168		 * but we can't hold any locks when we do this because
169		 * sess_rele() may need to acquire pidlock.
170		 */
171		mutex_exit(&sp->s_lock);
172		sess_rele(sp, B_FALSE);
173
174		if (got_sig)
175			return (NULL);
176	}
177
178	/* whew, we finally got a hold */
179	sp->s_cnt++;
180	sp->s_ref++;
181	mutex_exit(&sp->s_lock);
182	mutex_exit(&p->p_splock);
183	return (sp);
184}
185
186void
187tty_rele(sess_t *sp)
188{
189	/* make sure the caller isn't holding locks they shouldn't */
190	ASSERT(MUTEX_NOT_HELD(&pidlock));
191
192	mutex_enter(&sp->s_lock);
193	if ((--sp->s_cnt) == 0)
194		cv_broadcast(&sp->s_cnt_cv);
195	mutex_exit(&sp->s_lock);
196
197	sess_rele(sp, B_FALSE);
198}
199
200void
201sess_create(void)
202{
203	proc_t *p = curproc;
204	sess_t *sp, *old_sp;
205
206	sp = kmem_zalloc(sizeof (sess_t), KM_SLEEP);
207
208	mutex_init(&sp->s_lock, NULL, MUTEX_DEFAULT, NULL);
209	cv_init(&sp->s_cnt_cv, NULL, CV_DEFAULT, NULL);
210
211	/*
212	 * we need to grap p_lock to protect p_pgidp because
213	 * /proc looks at p_pgidp while holding only p_lock.
214	 *
215	 * we don't need to hold p->p_sessp->s_lock or get a hold on the
216	 * session structure since we're not actually updating any of
217	 * the contents of the old session structure.
218	 */
219	mutex_enter(&pidlock);
220	mutex_enter(&p->p_lock);
221	mutex_enter(&p->p_splock);
222
223	pgexit(p);
224
225	sp->s_sidp = p->p_pidp;
226	sp->s_ref = 1;
227	sp->s_dev = NODEV;
228
229	old_sp = p->p_sessp;
230	p->p_sessp = sp;
231
232	pgjoin(p, p->p_pidp);
233	PID_HOLD(p->p_pidp);
234
235	mutex_exit(&p->p_splock);
236	mutex_exit(&p->p_lock);
237	mutex_exit(&pidlock);
238
239	sess_rele(old_sp, B_FALSE);
240}
241
242/*
243 * Note that sess_ctty_clear() resets all the fields in the session
244 * structure but doesn't release any holds or free any objects
245 * that the session structure might currently point to.  it is the
246 * callers responsibility to do this.
247 */
248static void
249sess_ctty_clear(sess_t *sp, stdata_t *stp)
250{
251	/*
252	 * Assert that we hold all the necessary locks.  We also need
253	 * to be holding proc_t->p_splock for the process associated
254	 * with this session, but since we don't have a proc pointer
255	 * passed in we can't assert this here.
256	 */
257	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
258	    MUTEX_HELD(&sp->s_lock));
259
260	/* reset the session structure members to defaults */
261	sp->s_sighuped = B_FALSE;
262	sp->s_dev = NODEV;
263	sp->s_vp = NULL;
264	sp->s_cred = NULL;
265
266	/* reset the stream session and group pointers */
267	stp->sd_pgidp = NULL;
268	stp->sd_sidp = NULL;
269}
270
271static void
272sess_ctty_set(proc_t *p, sess_t *sp, stdata_t *stp)
273{
274	cred_t	*crp;
275
276	/* Assert that we hold all the necessary locks. */
277	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
278	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
279
280	/* get holds on structures */
281	mutex_enter(&p->p_crlock);
282	crhold(crp = p->p_cred);
283	mutex_exit(&p->p_crlock);
284	PID_HOLD(sp->s_sidp);	/* requires pidlock */
285	PID_HOLD(sp->s_sidp);	/* requires pidlock */
286
287	/* update the session structure members */
288	sp->s_vp = makectty(stp->sd_vnode);
289	sp->s_dev = sp->s_vp->v_rdev;
290	sp->s_cred = crp;
291
292	/* update the stream emebers */
293	stp->sd_flag |= STRISTTY;	/* just to be sure */
294	stp->sd_sidp = sp->s_sidp;
295	stp->sd_pgidp = sp->s_sidp;
296}
297
298int
299strctty(stdata_t *stp)
300{
301	sess_t		*sp;
302	proc_t		*p = curproc;
303	boolean_t	got_sig = B_FALSE;
304
305	/*
306	 * We are going to try to make stp the default ctty for the session
307	 * associated with curproc.  Not only does this require holding a
308	 * bunch of locks but it also requires waiting for any outstanding
309	 * holds on the session structure (acquired via tty_hold()) to be
310	 * released.  Hence, we have the following for(;;) loop that will
311	 * acquire our locks, do some sanity checks, and wait for the hold
312	 * count on the session structure to hit zero.  If we get a signal
313	 * while waiting for outstanding holds to be released then we abort
314	 * the operation and return.
315	 */
316	for (;;) {
317		mutex_enter(&stp->sd_lock);	/* protects sd_pgidp/sd_sidp */
318		mutex_enter(&pidlock);		/* protects p_pidp */
319		mutex_enter(&p->p_splock);	/* protects p_sessp */
320		sp = p->p_sessp;
321		mutex_enter(&sp->s_lock);	/* protects sp->* */
322
323		if (((stp->sd_flag & (STRHUP|STRDERR|STWRERR|STPLEX)) != 0) ||
324		    (stp->sd_sidp != NULL) ||		/* stp already ctty? */
325		    (p->p_pidp != sp->s_sidp) ||	/* we're not leader? */
326		    (sp->s_vp != NULL)) {		/* session has ctty? */
327			mutex_exit(&sp->s_lock);
328			mutex_exit(&p->p_splock);
329			mutex_exit(&pidlock);
330			mutex_exit(&stp->sd_lock);
331			return (ENOTTY);
332		}
333
334		/* sanity check.  we can't be exiting right now */
335		ASSERT(!sp->s_exit);
336
337		/*
338		 * If no one else has a hold on this session structure
339		 * then we now have exclusive access to it, so break out
340		 * of this loop and update the session structure.
341		 */
342		if (sp->s_cnt == 0)
343			break;
344
345		/* need to hold the session so it can't be freed */
346		sp->s_ref++;
347
348		/* ain't locking order fun? */
349		mutex_exit(&p->p_splock);
350		mutex_exit(&pidlock);
351		mutex_exit(&stp->sd_lock);
352
353		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock))
354			got_sig = B_TRUE;
355		mutex_exit(&sp->s_lock);
356		sess_rele(sp, B_FALSE);
357
358		if (got_sig)
359			return (EINTR);
360	}
361
362	/* set the session ctty bindings */
363	sess_ctty_set(p, sp, stp);
364
365	mutex_exit(&sp->s_lock);
366	mutex_exit(&p->p_splock);
367	mutex_exit(&pidlock);
368	mutex_exit(&stp->sd_lock);
369	return (0);
370}
371
372/*
373 * freectty_lock() attempts to acquire the army of locks required to free
374 * the ctty associated with a given session leader process.  If it returns
375 * successfully the following locks will be held:
376 *	sd_lock, pidlock, p_splock, s_lock
377 *
378 * as a secondary bit of convenience, freectty_lock() will also return
379 * pointers to the session, ctty, and ctty stream associated with the
380 * specified session leader process.
381 */
382static boolean_t
383freectty_lock(proc_t *p, sess_t **spp, vnode_t **vpp, stdata_t **stpp,
384    boolean_t at_exit)
385{
386	sess_t		*sp;
387	vnode_t		*vp;
388	stdata_t	*stp;
389
390	mutex_enter(&pidlock);			/* protect p_pidp */
391	mutex_enter(&p->p_splock);		/* protect p->p_sessp */
392	sp = p->p_sessp;
393	mutex_enter(&sp->s_lock);		/* protect sp->* */
394
395	if ((sp->s_sidp != p->p_pidp) ||	/* we're not leader? */
396	    (sp->s_vp == NULL)) {		/* no ctty? */
397		mutex_exit(&sp->s_lock);
398		mutex_exit(&p->p_splock);
399		mutex_exit(&pidlock);
400		return (B_FALSE);
401	}
402
403	vp = sp->s_vp;
404	stp = sp->s_vp->v_stream;
405
406	if (at_exit) {
407		/* stop anyone else calling tty_hold() */
408		sp->s_exit = B_TRUE;
409	} else {
410		/*
411		 * due to locking order we have to grab stp->sd_lock before
412		 * grabbing all the other proc/session locks.  but after we
413		 * drop all our current locks it's possible that someone
414		 * could come in and change our current session or close
415		 * the current ctty (vp) there by making sp or stp invalid.
416		 * (a VN_HOLD on vp won't protect stp because that only
417		 * prevents the vnode from being freed not closed.)  so
418		 * to prevent this we bump s_ref and s_cnt here.
419		 *
420		 * course this doesn't matter if we're the last thread in
421		 * an exiting process that is the session leader, since no
422		 * one else can change our session or free our ctty.
423		 */
424		sp->s_ref++;	/* hold the session structure */
425		sp->s_cnt++;	/* protect vp and stp */
426	}
427
428	/* drop our session locks */
429	mutex_exit(&sp->s_lock);
430	mutex_exit(&p->p_splock);
431	mutex_exit(&pidlock);
432
433	/* grab locks in the right order */
434	mutex_enter(&stp->sd_lock);		/* protects sd_pgidp/sd_sidp */
435	mutex_enter(&pidlock);			/* protect p_pidp */
436	mutex_enter(&p->p_splock);		/* protects p->p_sessp */
437	mutex_enter(&sp->s_lock);		/* protects sp->* */
438
439	/* if the session has changed, abort mission */
440	if (sp != p->p_sessp) {
441		/*
442		 * this can't happen during process exit since we're the
443		 * only thread in the process and we sure didn't change
444		 * our own session at this point.
445		 */
446		ASSERT(!at_exit);
447
448		/* release our locks and holds */
449		mutex_exit(&sp->s_lock);
450		mutex_exit(&p->p_splock);
451		mutex_exit(&pidlock);
452		mutex_exit(&stp->sd_lock);
453		tty_rele(sp);
454		return (B_FALSE);
455	}
456
457	/*
458	 * sanity checks.  none of this should have changed since we had
459	 * holds on the current ctty.
460	 */
461	ASSERT(sp->s_sidp == p->p_pidp);	/* we're the leader */
462	ASSERT(sp->s_vp != NULL);		/* a ctty exists */
463	ASSERT(vp == sp->s_vp);
464	ASSERT(stp == sp->s_vp->v_stream);
465
466	/* release our holds */
467	if (!at_exit) {
468		if ((--(sp)->s_cnt) == 0)
469			cv_broadcast(&sp->s_cnt_cv);
470		sp->s_ref--;
471		ASSERT(sp->s_ref > 0);
472	}
473
474	/* return our pointers */
475	*spp = sp;
476	*vpp = vp;
477	*stpp = stp;
478
479	return (B_TRUE);
480}
481
482/*
483 * Returns B_FALSE if no signal is sent to the process group associated with
484 * this ctty.  Returns B_TRUE if a signal is sent to the process group.
485 * If it return B_TRUE it also means that all the locks we were holding
486 * were dropped so that we could send the signal.
487 */
488static boolean_t
489freectty_signal(proc_t *p, sess_t *sp, stdata_t *stp, boolean_t at_exit)
490{
491	/* Assert that we hold all the necessary locks. */
492	ASSERT(MUTEX_HELD(&stp->sd_lock) && MUTEX_HELD(&pidlock) &&
493	    MUTEX_HELD(&p->p_splock) && MUTEX_HELD(&sp->s_lock));
494
495	/* check if we already signaled this group */
496	if (sp->s_sighuped)
497		return (B_FALSE);
498
499	sp->s_sighuped = B_TRUE;
500
501	if (!at_exit) {
502		/*
503		 * once again, we're about to drop our army of locks and we
504		 * don't want sp or stp to be freed.  (see the comment in
505		 * freectty_lock())
506		 */
507		sp->s_ref++;	/* hold the session structure */
508		sp->s_cnt++;	/* protect vp and stp */
509	}
510
511	/* can't hold these locks while calling pgsignal() */
512	mutex_exit(&sp->s_lock);
513	mutex_exit(&p->p_splock);
514	mutex_exit(&pidlock);
515
516	/* signal anyone in the foreground process group */
517	pgsignal(stp->sd_pgidp, SIGHUP);
518
519	/* signal anyone blocked in poll on this stream */
520	if (!(stp->sd_flag & STRHUP))
521		strhup(stp);
522
523	mutex_exit(&stp->sd_lock);
524
525	/* release our holds */
526	if (!at_exit)
527		tty_rele(sp);
528
529	return (B_TRUE);
530}
531
532int
533freectty(boolean_t at_exit)
534{
535	proc_t		*p = curproc;
536	stdata_t	*stp;
537	vnode_t		*vp;
538	cred_t		*cred;
539	sess_t		*sp;
540	struct pid	*pgidp, *sidp;
541	boolean_t	got_sig = B_FALSE;
542
543	/*
544	 * If the current process is a session leader we are going to
545	 * try to release the ctty associated our current session.  To
546	 * do this we need to acquire a bunch of locks, signal any
547	 * processes in the forground that are associated with the ctty,
548	 * and make sure no one has any outstanding holds on the current
549	 * session * structure (acquired via tty_hold()).  Hence, we have
550	 * the following for(;;) loop that will do all this work for
551	 * us and break out when the hold count on the session structure
552	 * hits zero.
553	 */
554	for (;;) {
555		if (!freectty_lock(p, &sp, &vp, &stp, at_exit))
556			return (EIO);
557
558		if (freectty_signal(p, sp, stp, at_exit)) {
559			/* loop around to re-acquire locks */
560			continue;
561		}
562
563		/*
564		 * Only a session leader process can free a ctty.  So if
565		 * we've made it here we know we're a session leader and
566		 * if we're not actively exiting it impossible for another
567		 * thread in this process to be exiting.  (Because that
568		 * thread would have already stopped all other threads
569		 * in the current process.)
570		 */
571		ASSERT(at_exit || !sp->s_exit);
572
573		/*
574		 * If no one else has a hold on this session structure
575		 * then we now have exclusive access to it, so break out
576		 * of this loop and update the session structure.
577		 */
578		if (sp->s_cnt == 0)
579			break;
580
581		if (!at_exit) {
582			/* need to hold the session so it can't be freed */
583			sp->s_ref++;
584		}
585
586		/* ain't locking order fun? */
587		mutex_exit(&p->p_splock);
588		mutex_exit(&pidlock);
589		mutex_exit(&stp->sd_lock);
590
591		if (at_exit) {
592			/*
593			 * if we're exiting then we can't allow this operation
594			 * to fail so we do a cw_wait() instead of a
595			 * cv_wait_sig().  if there are threads with active
596			 * holds on this ctty that are blocked, then
597			 * they should only be blocked in a cv_wait_sig()
598			 * and hopefully they were in the foreground process
599			 * group and recieved the SIGHUP we sent above.  of
600			 * course it's possible that they weren't in the
601			 * foreground process group and didn't get our
602			 * signal (or they could be stopped by job control
603			 * in which case our signal wouldn't matter until
604			 * they are restarted).  in this case we won't
605			 * exit until someone else sends them a signal.
606			 */
607			cv_wait(&sp->s_cnt_cv, &sp->s_lock);
608			mutex_exit(&sp->s_lock);
609			continue;
610		}
611
612		if (!cv_wait_sig(&sp->s_cnt_cv, &sp->s_lock)) {
613			got_sig = B_TRUE;
614		}
615
616		mutex_exit(&sp->s_lock);
617		sess_rele(sp, B_FALSE);
618
619		if (got_sig)
620			return (EINTR);
621	}
622	ASSERT(sp->s_cnt == 0);
623
624	/* save some pointers for later */
625	cred = sp->s_cred;
626	pgidp = stp->sd_pgidp;
627	sidp = stp->sd_sidp;
628
629	/* clear the session ctty bindings */
630	sess_ctty_clear(sp, stp);
631
632	/* wake up anyone blocked in tty_hold() */
633	if (at_exit) {
634		ASSERT(sp->s_exit);
635		sp->s_exit = B_FALSE;
636		cv_broadcast(&sp->s_exit_cv);
637	}
638
639	/* we can drop these locks now */
640	mutex_exit(&sp->s_lock);
641	mutex_exit(&p->p_splock);
642	mutex_exit(&pidlock);
643	mutex_exit(&stp->sd_lock);
644
645	/* This is the only remaining thread with access to this vnode */
646	(void) VOP_CLOSE(vp, 0, 1, (offset_t)0, cred, NULL);
647	VN_RELE(vp);
648	crfree(cred);
649
650	/* release our holds on assorted structures and return */
651	mutex_enter(&pidlock);
652	PID_RELE(pgidp);
653	PID_RELE(sidp);
654	mutex_exit(&pidlock);
655
656	return (1);
657}
658
659/*
660 *	++++++++++++++++++++++++
661 *	++  SunOS4.1 Buyback  ++
662 *	++++++++++++++++++++++++
663 *
664 * vhangup: Revoke access of the current tty by all processes
665 * Used by privileged users to give a "clean" terminal at login
666 */
667int
668vhangup(void)
669{
670	if (secpolicy_sys_config(CRED(), B_FALSE) != 0)
671		return (set_errno(EPERM));
672	/*
673	 * This routine used to call freectty() under a condition that
674	 * could never happen.  So this code has never actually done
675	 * anything, and evidently nobody has ever noticed.
676	 */
677	return (0);
678}
679
680dev_t
681cttydev(proc_t *pp)
682{
683	sess_t	*sp;
684	dev_t	dev;
685
686	mutex_enter(&pp->p_splock);	/* protects p->p_sessp */
687	sp = pp->p_sessp;
688
689#ifdef DEBUG
690	mutex_enter(&sp->s_lock);	/* protects sp->* */
691	if (sp->s_vp == NULL)
692		ASSERT(sp->s_dev == NODEV);
693	else
694		ASSERT(sp->s_dev != NODEV);
695	mutex_exit(&sp->s_lock);
696#endif /* DEBUG */
697
698	dev = sp->s_dev;
699	mutex_exit(&pp->p_splock);
700	return (dev);
701}
702
703void
704ctty_clear_sighuped(void)
705{
706	ASSERT(MUTEX_HELD(&pidlock) || MUTEX_HELD(&curproc->p_splock));
707	curproc->p_sessp->s_sighuped = B_FALSE;
708}
709