V10/history/ix/src/doc/secunix/fp2

.NH 1
Details
.XL yJ
.PP
This section specifies
security check calculations (\*(yc),
the effect of label changes and $SIGLAB$ (\*(y0),
the privilege mechanism (\*(yQ),
labels of special files (\*(y1),
new system calls (\*(yN),
and special security behavior of system calls (\*(yY, \*(yG).
.NH 2
Security checks
.XL yc
.PP
Standard security checks are made for system calls that refer to
files or file descriptors.
Each system call is first subjected to the security check calculation
specified in table \*(zT below, as elaborated
in sections \*(yY, \*(yN, and \*(yG.
Failed security checks return error $ELAB$ (\*(yT)
or $EPRIV$ (\*(yU) unless otherwise specified.
.PP
Table \*(zT summarizes the data flows caused by each system
call, lists the standard checks performed for each call,
and indicates renewal possibilities.
Some calls have special checks, which are described in
sections referred to in the Notes column.
The standard checks are
.RS
.IP READ($d$) 10
for a
.I read
call with descriptor $d$ (\*(yr)
.IP WRITE($d$) 10
for a
.I write
call with descriptor $d$ (\*(yw)
.IP R($x$) 10
fre retrieving other data from an object $x$, as in
.I stat
(\*(zr)
.IP RS($d$) 10
for retrieving the seek pointer of file descriptor $d$ (\*(yt)
.IP W($x$) 10
for assigning other data to an object $x$ (\*(zw)
.IP WS($d$) 10
for assigning to the seek pointer of file descriptor $d$ (\*(zs)
.IP RD($f$) 10
for interpreting a file name $f$ (\*(zR)
.IP WRD($f$) 10
for writing a file name $f$ in a directory (\*(zW)
.IP P($f$) 10
process-exclusive access (\*(yx)
.IP $Tlog$ 10
$Tlog (p)$
must be true
.IP $Textern$ 10
$Textern (p)$
must be true
.IP $Tuarea$ 10
$Tuarea (p)$
must be true if superuser status is required
.RE
.PP
Information (usually concerning access rights)
obtained only through error returns
is not normally subject to security checks.
Information (such as link counts) created 
as side effects of system calls is checked
unless checking would seriously impair utility.
.PP
Data flows marked * are not checked.
Bracketed entries in the data flow column refer to
covert flows that are checked, although
they do not count as data flows in
the strict sense of \*(zD.
.SP
For example, it may be possible to infer the value of a seek pointer
from the bits delivered by a read, although the
value is not delivered directly.
Hence a flow $[s -> p]$
is attributed to the
.I read
system call.
.PP
Symbols $f$, $d$, and $s$ in the body of the table denote the
file, file descriptor, and seek pointer (if any) referred to by
the arguments of the system call.
Thus for a system call with a file descriptor argument $d$, 
the symbol $f$ means $f(d)$
and $s$ means $s(d)$.
Subscripts distinguish multiple file arguments.
Symbol $q$ refers to another process, in particular the
new process after
.I exec ;
$u(p)$ and $u(q)$ denote
the user areas of the respective processes; $u$ is short for $u(p)$.
The remaining codes used in the table are
.RS
.IP i 8
information about inode inferable through error return
.IP p 8
implicit write to
.CW /proc
or to inode of \f(CW/proc/\fIp\fR
.IP u 8
implicit write in uarea, readable through
.CW /proc/\c
.I p ;
contrast with explicit write $p~->~u$
.IP X 8
abolish
.RE
.SP
Consider \f(CWchmod("/etc/passwd",0666)\fR.
It is necessary to read directory
.CW /etc
to find the file
.CW passwd
(RD).
It is also necessary to read the inode of
.CW /etc/passwd
to determine whether the process has the right to change it.
This is an implicit read (i).
If permission is granted, the given mode is written into the inode
(W($f$)).
Finally the inode change date
is set as a side effect.
.QE
.SP
To do
.CW unlink("/etc/passwd") ,
it is necessary to read directory
.CW /etc
to find the file
.CW passwd
(RD, implied by WRD)
and to read the inode of
.CW /etc/passwd
to determine whether the process has the right to change it (i).
If the link count does not go to zero, the new count must be
written and the inode change time updated (W).
Finally the entry for
.CW passwd
must be deleted from directory
.CW /etc
and the modification and change times for
.CW /etc
must be updated
(WRD).
.PP
The behavior of system calls with no marks in
the table is unaffected.
When both security and permissions are checked on a given
file, security is checked first.
An implementer may choose to return
a search permission error encountered early in a path
even if a security error would occur later in the path.
.ne 1i
.NH 3
Table of system calls
.XL zT
.LP
.TS H
box center;
lc|c|c|c
lc|c|l|l
lc|r|l|l.
_
System	Priv	Data	Checks	Notes
call		flows
=
.TH
access($f,m$)			RD($f$)	i
acct($f$)	$Tlog$		RD($f$)	i \*(z4
alarm($n$)							u
biasclock($n$)				\*(yB
brk($n$)		$p -> u$
_
chdir($f$)		RD($f$)	iu
chmod($f,m$)		$p -> f$	RD($f$),W($f$)	i \*(zm
chown($f,n sub 1 ,n sub 2$)		$p -> f$	RD($f$),W($f$)		i \*(zn
chroot				X		
close($d$)				u \*(z7
_
creat($f,m$)		$p -> f$	P($f$),WRD($f$)	iu \*(yf
dirread($d$)		$f -> p$	READ($d$)	i \*(yR
		$[f -> s ]$
		$[s -> p ]$
dup($d$)				u \*(zd
dup2($d sub 1 ,d sub 2$)				u \*(zd
exec($f,a$)		$p -> q$	RD($f$)	iup \*(y6
		$f -> q$
		$u(p) -> u(q)*$
_
exec($f,0$)		$f -> q$	RD($f$)	iup \*(y6
		$u(p) -> u(q)*$
exit($v$)				p \*(yg
fchmod($d,m$)		$p -> f$	W($f$)
fchown($d,n sub 1 ,n sub 2$)		$p -> f$	W($f$)
fgetflab($d,l$)		$f -> p$	R($f$)	\*(zG
_
fmount($n sub 1 ,d,f,n sub 2$)	$Textern$	$p -> f$	RD($f$),W($f$)	i \*(z8
fmount5($n,d,f,n,c$)	$Textern$	$p - >f$	RD($f$),W($f$)	i \*(z8
fork()		$u(p) -> u(q)$		up
fsetflab($d,l$)		$p -> f$		\*(y2
fstat($d,b$)		$f -> p$		R($f$)
_
ftime($b$)
funmount($f$)		$p -> f$	RD($f$),W($f$)
getegid()		$u -> p$
geteuid()		$u -> p$
getflab($f,l$)		$f -> p$	RD($f$),R($f$)	\*(zG
_
getgid()		$u -> p$
getgroups($n,b$)				X
getlogname($b$)		$u -> p$
getpgrp($p$)		$u -> p*$					\*(yj
getpid()		$u -> p$
_
getplab($l,c$)			RS($C(p)$)	\*(zE
getppid()		$u -> p$
getuid()		$u -> p$
ioctl($d,n,b$)		various	P($f$),etc.	\*(yG
kill($q,n$)		$[p -> q]$		p \*(ye
_
labmount($d,l$)		$f -> p$		\*(zl
link($f sub 1, f sub 2$)		$[p -> f sub 1 ]$	RD($f sub 1$),WRD($f sub 2$),W($f sub 1$)
lock($n$)				u
lseek($d,n sub 1 ,n sub 2$)		$p -> s$	P($f$),WS($d$),RS($d$)	\*(yh
		$s -> p$
lstat($f,b$)		$f -> p$	RD($f$),R($f$)
_
mkdir($f,m$)		$p -> f*$	WRD($f$)	\*(yM
mknod($f,m,b$)		$p -> f*$	WRD($f$)	\*(yM
nap($n$)
nice($n$)				u \*(z2
nochk($d,m$)				\*(zk
_
open($f,n$)			RD($f$)	iu
pause()				u				u
pipe()				u
profil($b,n sub 1 ,n sub 2 ,n sub 3$)
read($d,b,n$)		$f -> p$	P($f$),READ($d$)	\*(yR
		$[f -> s]$
		$[s -> p]$
	$[p -> s]$
_
readlink($f,b,n$)		$f -> p$	RD($f$),R($f$)
reboot($n$)		
rmdir($f$)		WRD($f$)				i \*(yu
seek($d,n sub 1 , n sub 2$)		$p -> s$	P($f$),WS($d$)	\*(yH
select($n sub 1 , b sub 1 ,b sub 2 ,n sub 2$)				\*(yA
_
setflab($f,l$)		$p -> f$	RD($f$)	i \*(y2
setgid($n$)	$Tuarea$	$p -> u$
setgroups($n,b$)			X
setlogname($b$)	$Tuarea$	$p -> u$
setpgrp($p,0$)			u \*(yj
_
setpgrp($p,n$)	$Tuarea$	$p -> u$		\*(yj
setplab($l,c$)				u \*(yd
setruid($n$)	$Tuarea$	$p -> u$		up
setuid($n$)	$Tuarea$	$p -> u$		up
signal($n,g$)				u \*(ye
_
stat($f,b$)		$f -> p$	RD($f$),R($f$)
stime($b$)				\*(yB
symlink($f sub 1, f sub 2$)		$p -> f sub 2$	WRD($f$),W($f sub 2$)	i
sync()
syslog($n sub 1 , n sub 2 , n sub 3$)	$Tlog$	various		\*(yl
_
tell($d$)		$s -> p$	RS($d$)	\*(yI
time($b$)
times($b$)				\*(yC
umask($m$)		$p -> u$					u \*(zu
unlink($f$)		$[p -> f]$	WRD($f$)	i \*(yu
_
unsafe($n,b sub 1 ,b sub 2$)				\*(y8
utime($f,b$)		$p -> f$	RD($f$),W($f$)		i
vadvise($n$)
vlimit($d,n sub 1 , n sub 2$)	$Tuarea$	$p -> u$
vswapon($f$)				\*(yK
_
vtimes($b sub 1 ,b sub 2$)	sys$->p$				\*(yC
wait($b$)		$q -> p$		u \*(yg
write($d,b,n$)		$p -> f$	P($f$),WRITE($d$),W($f$)	\*(yW
		$[p -> s]$
		$[s -> f]$
_
.TE
.NH 3
Standard check computations
.PP
Security checks enforce
.I "critical inequalities
among labels as required by the ``Data flow''
column of table \*(zT.
An inequality need not be checked if it is
overridden by privilege, if it involves a seek pointer
in a stream, or if its truth is implied by side knowledge.
Such knowledge may be obtained
from safe bits, or from the partial transitivity of $<=$ and
the invariants
.EQ I
lpile {
{ L(p)~<=~C(p) } above
{ L(p)~member~LL } above
{ C(p)~member~LL } above
{ L(s)~member~LL ,~ roman "if the seek pointer is defined" }
}
.EN
.NH 3
READ($d$): Check for the read system call
.XL yr
.LP
Let file descriptor $d$ name $(p,^ s,^ f)$.
The critical inequalities are
.EQ I
lpile {
{ L(f)~<=~C(f) } above
{ L(f)~<=~L(s) } above
{ L(f)~<=~L(p) } above
{ L(f)~<=~C(p) } above
{ L(s)~<=~L(p) } above
{ L(s)~<=~C(p) } above
{ L(p)~<=~L(s) }
}
.EN
.SP
Seek pointer inequalities follow from the observation that
reading entails direct flow $f~->~p$ (the bits)
and covert flows $[s~->~p]$ (which bits),
$[p~->~s]$ (how many) and
$[f~->~s]$ (at end of file).
.LP
Do the following check as each block of data is copied to user space.
.IP
Let $M~=~max (L(p),^L(s),^L(f))$.
.IP
If $d$ is marked safe-to-read the check succeeds.
.IP
Otherwise, if $Tnochk (p)$ and $d$ is marked exempt
the check succeeds.
.IP
Otherwise, if the critical inequalities hold the check succeeds.
.IP
Otherwise, if $L(f)~!<=~C(f)$ then $error$.
.IP
Otherwise, if $M~!<=~C(p)$ then $error$.
.IP
Otherwise, if $L(p)~!=~M$ and $F(p)~!=~LOOSE$ then $error$.
.IP
Otherwise, establish the critical inequalities:
.RS
.IP
If $L(p)~!=~M$ set $L(p)~:=~M$ and propagate with CHP($p$), \*(yP.
.IP
If $L(s)~!=~M$ set $L(s)~:=~M$ and propagate with CHS($s$), \*(ys.
.RE
.LP
If no error occurred mark $d$ safe-to-read.
.NH 3
WRITE($d$): Check for the write system call
.XL yw
.LP
Let file descriptor $d$ name $(p,^ s,^ f)$.
For streams, $s$ doesn't matter; we suppose $L(s)~=~L(f)$.
The critical inequalities are
.DS I
$L(p)~<=~C(f)$
$L(s)~<=~C(f)$
$L(f)~<=~C(p)$
$L(s)~<=~L(f)$
$L(s)~<=~C(p)$
$L(p)~<=~L(s)$
$L(p)~<=~L(f)$
.DE
.SP
Seek pointer inequalities follow from the observation that
writing entails direct flows $p~->~f$ (the bits) 
and covert flows $[s~->~f]$ (which bits) and
$[p~->~s]$ (how many).
Inequality $L(s)~<=~C(p)$ prevents $p$ from interfering
with a higher process through a shared seek pointer.
.LP
Do the following check as each block of data
is copied out of user space.
.IP
Let $M~=~max (L(p),^L(s),^L(f))$ and $C~=~min (C(p),^C(f))$.
.IP
If $d$ is marked safe-to-write the check succeeds.
.IP
Otherwise, if $T(f)$ then $error$.
.IP
Otherwise, if $Tnochk (p)$ and $d$ is marked exempt
the check succeeds.
.IP
Otherwise, if the critical inequalities hold
the check succeeds.
.IP
Otherwise, if $M~!<=~ C$ then $error$.
.IP
Otherwise, if $f$ is the process file for process $q$
and $M~!<=~L(q)$ then $error$.
.IP
Otherwise, if $M~!<=~L(f)$ and $F(f)~!=~LOOSE$ then $error$.
.IP
Otherwise, establish the critical inequalities:
.RS
.IP
If $L(p)~!<=~L(s)$ set $L(s)~:=~max (L(p),^L(s))$ and propagate with
CHS($s$), \*(ys.
.IP
If $L(f)~!=~M$ set $L(f)~:=~M$ and propagate with CHF($f$), \*(yF.
.RE
.LP
On error, raise signal $SIGPIPE$.
.LP
Otherwise, mark $d$ safe-to-write.
.SP
Some of the complexity of the check arises from
the possibility that $L(f)~=~YES$.
.SP
$SIGPIPE$ has nothing to do with security.
Just as with broken pipes, it stops processes when their output is
unexpectedly being thrown away.
.SP
.I Write
calls may fail with $ELAB$ or $EPERM$ even though the corresponding
.I open
calls succeed; programmers should always take care to check for
.I write
errors explicitly.
.NH 3
R($f$): Check for read-like calls on a file
.XL zr
.LP
The critical inequalities are
.DS I
$L(f)~<=~C(f)$
$L(f)~<=~L(p)$
$L(p)~<=~C(p)$
.DE
.LP
If $Tnochk (p)$ the check succeeds.
.LP
Otherwise, if the critical inequalities hold the check succeeds.
.LP
Otherwise, if $L(f)~!<=~inf (C(f),^C(p))$ then $error$.
.LP
Otherwise, if $F(p)~!=~LOOSE$ then $error$.
.LP
Otherwise, establish the critical inequalities:
.IP
Set $L(p)~:=~max (L(p),^L(f))$ and propagate with CHP($p$), \*(yP.
.RE
.SP
The capability to omit checks overrides.
If the process is not cleared to read the object,
raise the process label.
.NH 3
RS($x$): Check for other read-like calls
.XL yt
.LP
If $x$ is a file descriptor $d$ let $s$ be $s(d)$.
.LP
Otherwise let $s$ be $C(p)$.
.LP
The critical inequalities are
.DS I
$L(s)~<=~L(p)$	
$L(p)~<=~C(p)$
.DE
.LP
If $Tnochk (p)$ the check succeeds.
.LP
Otherwise, if $d$ is marked safe-to-read the check succeeds.
.LP
Otherwise, if the critical inequalities hold the check succeeds.
.LP
Otherwise, if $L(s)~!<=~C(p)$ or $F(p)~!=~LOOSE$ then $error$.
.LP
Otherwise, establish the critical inequalities:
.IP
Set $L(p)~:=~max (L(p),^L(s))$ and propagate with CHP($p$), \*(yP.
.SP
This check is used only by
.I lseek,
\*(yh,
.I tell,
\*(yI, and
.I getplab,
\*(zE.
For
.I getplab
the label $L(s)$ is not the ceiling label $C(p)$, but is
instead the label of the ceiling, $L(C(p))$.
.NH 3
W($f$): Check for write-like calls on a file
.XL zw
.LP
The critical inequalities are
.DS I
$L(p)~<=~C(f)$
$L(f)~<=~C(p)$	
$L(p)~<=~L(f)$
.DE
.LP
If $T(f)$ then $error$.
.LP
Otherwise, if $Tnochk (p)$ the check succeeds.
.LP
Otherwise, if the critical inequalities hold
the check succeeds.
.LP
Otherwise, if $f$ is the process file for
process $q$ and $L(p)~!<=~L(q)$ then $error$.
.LP
Otherwise, if $L(f)~=~NO$ then $error$.
.LP
Otherwise, if $F(x)~!=~LOOSE$ then $error$.
.LP
Otherwise establish the critical inequalities:
.IP
Set $L(f)~:=~max (L(p),^L(f))$ and propagate with CHF($f$), \*(yF.
.SP
Do not alter trusted files (except with a privileged
.I setflab,
\*(y2).
If the file is not cleared to receive from the process, attempt
to raise the file label.
.NH 3
WS($d$): Check for write-like calls on a seek pointer
.XL zs
.LP
Let $s~=~s(d)$.
.LP
The critical inequalities are
.DS I
$L(s)~<=~C(p)$
$L(p)~<=~L(s)$
.DE
.LP
If $d$ is marked safe-to-read the check succeeds.
.LP
Otherwise, if $Tnochk (p)$ and $d$ is marked
exempt the check succeeds.
.LP
Otherwise, if the critical inequalities hold
the check succeeds.
.LP
Otherwise, if the real value of $L(s)~=~max (L(p),^L(s))$
the check succeeds.
(An artificial value of $L(s)$ may have been
considered, \*(yH.)
.LP
Otherwise establish the critical inequalities:
.IP
Set $L(s)~:=~max (L(p),^L(s))$ and propagate with CHS($s$), \*(ys.
.SP
This check is used only by
.I lseek,
\*(yh, and
.I seek,
\*(yH.
.NH 3
RD: Interpret a file name
.XL zR
.LP
Let directories
$f sub i ,^i=1,2, ...$, be visited in tracing the pathname.
(The current directory is visited in tracing a relative pathname.)
.LP
For each $f sub i $ check R($f sub i$).
.IP
If the check fails then $error$.
.IP
Otherwise, if $L(f sub i )~!<=~C(f sub i )$ 
then $error$.
.SP
Directory search does not involve data flow, so
the label check is something of a frill.
However, it stops a potential 150bps covert channel
and prevents processes from peeking above their ceilings.
(Bit rates are explained at \*(yY.)
.SP
The labels of symbolic links, like their permissions, are
not checked;
the labels of the substituted pathname suffice.
.NH 3
WRD: Write in a directory
.XL zW
.LP
Directories are written whenever entries are made or deleted.
.LP
Perform the RD check.
Unless the directory is blind, \*(yb,
perform the W check for the directory as if it were a plain file
being written.
.SP
Although deleted entries are discernible only by
processes authorized to read a directory,
it would be prudent to clear them.
.NH 3
P($f$): Process-exclusive check
.XL yx
.LP
If $X(f)~!=~~UNPEXED$ and $p~!=~H(f)$ then error
.CW ECONC .
.LP
If $f$ is one end of a pipe and $p~=~H(f)$
.IP
If $X(f)~=~PEXED$ and $X(f sup prime )~!=~PEXED$
then error
.CW ECONC .
.IP
If $X(f)~=~UNPEXING$, then error
.CW ECONC .
.NH 3
Atomicity
.PP
No label changes shall occur between making a security check
and performing the action that the check is intended to protect.
It is permissible, however, for label changes to intervene
between checking a directory in a pathname and checking
an entry in the directory.
It may be necessary to check several times during an action,
for example during a
.I read
that incurs several disk transfers or page waits.
.NH 2
Label changes
.XL y0
.PP
The degree to which a label is changeable is determined by its
fixity, which takes on one of four values
$LOOSE$, $FROZEN$, $RIGID$, and $CONST$.
The owner of a file with a loose or frozen label may change the
fixity of that label to any value except $CONST$.
A process may change the fixity of its label back and forth
between loose and frozen.
There is no other way to change fixity.
.PP
.I Loose
labels can be changed by any process, either as a result of
an explicit label-changing system call or as a
side effect of a security check calculation.
.PP
.I Frozen
labels cannot be changed without first making the label loose.
.PP
.I Rigid
labels can be changed only by processes with capability
$Textern$.
The labels of external media are forced to be rigid.
.PP
.I Constant
labels never change.
Label constancy is a property of certain device files; see \*(y1.
.br
.ne3
.SP
External media have rigid labels because the label is the
only record of what the external destination is allowed
to see or of how incoming data should be classified.
.PP
Changes in labels are propagated by the following procedures.
The actions described are to be taken immediately and atomically,
even in the middle of a
.I read
or
.I write
call.
.NH 3
CHP($p$): Propagate change in process label
.XL yP
.LP
Clear the
safe-to-write bits on all file descriptors
in process $p$.
.LP
If $p$ has an associated process file $f$, propagate with CHF($f$),
avoiding further recursion.
.SP
The inequalities $L(p)~<=~L(f)$ are no longer known to be true
for files $f$ open in $p$.
When $p$ next attempts a write or when $p$ resumes an
incomplete write
WRITE will be checked afresh.
.NH 3
CHF($f$): Propagate change in file label
.XL yF
.LP
If $f$ has a disk inode, wait for it to be updated.
.LP
For all file descriptors $d$ such that $f(d)~=~f$
.IP
Clear the
safe-to-read and safe-to-write bits on $d$.
.IP
Raise signal $SIGLAB$ in $p(d)$.
.LP
If $f$ is the process file for process $q$ then
propagate with CHP($q$), avoiding further recursion.
.LP
If $f$ is a pipe end with other end $f sup prime$ then
propagate with CHF($f sup prime$), avoiding further recursion.
.SP
To prevent inconsistency in case of a crash,
record the the label change on disk before transferring 
any data to the file.
.SP
The inequalities $L(f)~<=~L(q)$ are no longer known to be true in
other processes $q$.
When $q$ next attempts a read or when $q$ resumes an
incomplete read
READ will be checked afresh.
Similarly the inequalities $L(f)~<=~C(q)$ will be checked on
future writes.
.NH 3
CHS($s$): Propagate change in seek pointer label
.XL ys
.LP
Clear the 
safe-to-read and safe-to-write bits on all
file descriptors $d$ such that $s(d)~=~s$.
.LP
Raise signal $SIGLAB$ in $p(d)$ for all such $d$.
.NH 3
New file descriptors
.LP
Perform the following operations  on
every new file descriptor and on every descriptor copied between
processes, as by
.I exec
or
.CW FIORCVFD .
.LP
Clear the safe-to-read bit and safe-to-write bit.
.LP
Set the exempt bit.
.SP
These rules are conservative.
An implementer may copy the safe bits on descriptors cloned by
.I fork
or
.I dup
or by
.I open ing
a file descriptor file (\f(CW/dev/fd/*\fR).
The values of exempt bits do not matter unless $Tnochk (p)$ is true.
.NH 3
SIGLAB
.XL yS
.PP
Signal $SIGLAB$ is raised whenever a file descriptor changes label.
$SIGLAB$ is ignored if not caught.
.SP
A trusted process with $Tnochk$ capability would use
$SIGLAB$ to prevent unintended downgrading that
could occur if the labels of its input files changed.
The process would most likely freeze its own
label by setting $F(p)$ with
.I setplab 
(\*(yd), catch $SIGLAB$, and use
.I unsafe
(\*(y8) to isolate changes when they occur.
.NH 3
ELAB
.XL yT
.PP
Attempts to violate critical label inequalities
return error number $ELAB$.
.SP
Information may be communicated through $ELAB$:
Let process Low not be cleared for information known to
process High, i.e. $L( roman High )~!<=~L( roman Low )$,
and let $f$ be a file that Low is cleared for.
High either does or does not contaminate (raise the label of) $f$
by writing in it.
Low tries to read $f$, and discovers which
action High took according as it does or does not get
error $ELAB$.
The bandwidth of about 80bps might be reduced
by inserting delay when returning $ELAB$.
.SP
Denning describes a similar channel,
slower (10bps) but nonetheless elegant:|reference(Denning)
High either does or does not contaminate $f$.
Low writes a 1 in another low file $g$, then
reads $f$ and from the contents determines whether $f$
(and Low itself) has been contaminated.
If $f$ is not contaminated Low replaces the 1 by a 0.
The still low file $g$ now tells whether High did (1) or did
not (0) contaminate $f$, yet in neither case does there exist
a forbidden chain of data flow (in the sense of \*(y5)
from High to $g$, nor did any system call fail.
.SP
Denied writes on a file descriptor raise $SIGPIPE$; see \*(yw.
It may also be desirable to stop processes that attempt too many
security violations with a new, uncatchable signal, say $SIGSPY$.
.NH 3
EPRIV
.XL yU
.PP
Attempts to violate privilege rules
return error number $EPRIV$.
.SP
$EPRIV$ affords covert channels as does $ELAB$.
But processes that can modulate privilege must 
themselves be privileged; they have far more
potent ways to make covert channels.
.NH 2
Privileges
.XL yQ
.PP
The privilege bits $Vvec (p)$ and $Vvec (f)$ 
(recall that $Vvec (.)$ comprises both capabilities, $Tvec (.)$,
and licenses, $Uvec (.)$)
are stored
with $L(p)$ and $L(f)$.
The system calls 
.I "getplap, setplab, getflab, 
and
.I setflab
retrieve and change privileges
according to the policy set forth in \*(y9.
.LP
Privileges $Vvec (p)$ are inherited across
.I fork.
Licenses $Uvec (p)$ may be inherited across
.I exec ;
see \*(y6.
The initial capabilities of a child process $q$ executing 
a trusted file
$f$ from file system $FS(f)$ are given by
.EQ
Tvec (q)~=~Tvec (f) and Tvec FS(f) and
( Uvec (p)~or~Uvec (f) and Uvec (FS(f)) and Uvec sup 0 )
.EN 
.LP
The compile-time parameter $Uvec sup 0$ 
limits the self-licensing of files.
Currently only $Tnochk$, $Textern$, and $Tuarea$ may be self-licensed.
.SP
The capabilities and licenses of a file are masked by the
capability and license of its file system. 
A process obtains the capabilities it is licensed
for from the capabilities
of its executable file.
Capabilities are licensed either by inherited licenses
$Uvec (p)$ or by file licenses $Uvec (f)$.
.SP
The utility of the self-licensing limit $Uvec sup 0$ is questionable.
.NH 3
$bold {Cap sub extern}$
.XL zM
.PP
This capability is used to introduce 
foreign data into the system.
It is required for the
.I fmount
system call, to change labels away from $NO$, and to 
change rigid labels.
.SP
Capability $Textern$ is necessary to open external media or to modify
the label of an already open external medium.
In general a $Textern$ process will perform
some authentication protocol to determine the
proper label for the medium.
Automatic label setting won't work because the system
does not know what or who is out there.
.SP
Label $NO$ may be used to hide data from (almost) everybody.
Once marked $NO$, it can't be read by normal means until 
resurrected by $Textern$.
In effect data marked $NO$ has been removed from the system;
it comes back with a newly minted label as determined
by an administrative program with capability $Textern$.
.NH 3
$bold{Cap sub uarea}$
.PP
This capability permits modifying ``user-area'' data that
is readable by a descendent processes.
In general, only the superuser can write such data
(e.g.
.I setuid
and
.I setlogname ).
Thus $Tuarea$ enforces the notion that the superuser
has no business executing untrusted code.
.SP
Non-superuser writing in the user area is controlled
differently.
.I Umask
is censored when process labels drop; see\*(y6.
The process ceiling has its own label; see
\*(yd.
The notion of abolishing 
$Tuarea$ and instead labeling each user-area item is appealing.
However, we feared that this could lead
to the necessity for some other privilege to undo label
creep in the user area.
.NH 3
$bold{Cap sub nochk}$
.PP
This privilege allows a process to ignore label comparisons.
It is required for programs that inherently deal with
multilevel data, for example programs to repair 
or back up file systems, or programs
to handle multilevel multiplexed communication.
.PP
Capability $Tnochk$ overrides label checking only on
file descriptors marked exempt.
Fresh file descriptors are exempt; their
exempt status may be changed by the
.I nochk
system call; see \*(zk.
.SP
$Tnochk$ and $Textern$ may both be used to grant access to external media.
The difference is that $Tnochk$ gives access only to the process
that has the privilege\(emwhat is done with the access is under
its control\(emwhile $Textern$ makes the data available to
other processes.
.NH 3
$bold{Cap sub setpriv}$
.PP
This capability is required to change file privileges.
.SP
Programs with capability $Tsetpriv$ have the keys to
the kingdom; they must be very carefully designed.
.NH 3
$bold{Cap sub setlic}$
.PP
This capability is required to increase process licenses
or to change the process label and ceiling arbitrarily.
.SP
Capability $Tsetlic$
is used to set up user sessions.
.NH 3
$bold{Cap sub log}$
.PP
This capability is required to set up or change mandatory
auditing.
.NH 2
Special files
.XL y1
.PP
Because special files address resources with unusual properties
they may require unusual security considerations.
Some files, for example
.CW /dev/stdin ,
have the property that the file descriptor obtained by
.I open
refers to a different object than the file name does.
On such a file
.I fstat
and 
.I stat
may return completely differing data;
similarly 
.I fchmod
may not affect the original file that was opened to
obtain the file descriptor.
Some special files have rigid or constant labels; it is
impossible to change the fixity of these files.
.NH 3
Character special files.
.XL yp
.PP
On directories and
character special files the accept pex
indicator $APX(f)~=~false$ by default,
otherwise $APX(f)~=~true$ by default.
The setting of $APX(f)$ can be changed only for
character special files (\*(zx).
.SP
Process exclusive (pex) requests are designed to secure
trusted paths that are free from eavesdropping or
from forging or corruption of data by other processes (\*(zP).
A typical application is demanding a password from
a terminal.
If, however, the ``terminal'' is really a communication line
to another computer that is relaying the conversation,
process-exclusive access is not enough to guarantee the 
privacy of the password.
Only after a trusted process has somehow authenticated
the trustedness of the external path can 
exclusive use of the internal path assure privacy (\*(zp).
Initially, then, devices must
refuse to accept pex requests.
.NH 3
External media
.XL yD
.PP
External media comprise
all special files not otherwise discussed in section \*(y1.
They are files over whose contents the system
has not maintained complete control.
Examples are terminals, communication links,
tapes, and disks.
Pipes are not external media.
.PP
When an external medium is not open, its label is $NO$.
The label is rigid and
remains $NO$ after opening until it is
set away from $NO$ by a trusted process; see \*(y2.
Further openings see this new label.
The label reverts to $NO$ when no process has the file open.
.SP
In general authentication is required before
access may be granted to an external medium.
Authentication will be administered by trusted processes.
.SP
The fact that an inode shares a label with a file has
the unfortunate effect that
ordinary programs cannot read the properties of most device
files.
.NH 3
Streams
.PP
When a stream is created, its stream identifier is
initialized to the null string.
.PP
Both ends of a pipe stream share a single label,
which is initialized to 
$bottom$.
.PP
A device stream shares its label
with the device it is attached to.
.NH 3
Null and mem
.PP
The null device
.CW /dev/null
has constant label $YES$.
The 
.I stat
system call returns
dummy data for
.CW /dev/null .
.SP
This plugs covert channels through the mode bits of
.CW /dev/null .
.PP
The memory devices,
.CW /dev/mem 
and
.CW /dev/kmem ,
have constant label $NO$.
.NH 3
Process files
.PP
For each file $f$ in
.CW /proc
$Vvec (f)~=~Vvec (p)$, where $p$ is the corresponding process.
If $Tnochk (p)$ was ever true, $L(f)~=~top$, otherwise
$L(f)~=~L(p)$.
The process file disappears with the process, regardless of
whether it was trusted.
.LP
The virtual directory
.CW /proc
has a $RIGID$ bottom label and has universal read permission.
Creating a process does not count as writing in
.CW /proc .
.SP
The top label for $Tnochk$ processes prevents leaks
through debuggers.
.SP
By modulating the rate of process creation, unrelated
processes can communicate covertly through
.CW /proc
at a rate of a few bits per second.
.SP
When $Tnochk (p)$ is true upon
.I exec,
our implementation divorces the file privileges as well as the
file label from that of the process. 
The divorce happens when the inode is created, which
occurs on demand, not at process creation.
Subsequent changes in process privilege are not reflected
in the file.
Thus, the privileges of a process file labeled $top$
may not agree with those of the process.
But unless the file has capability $Tsetlic$, its privileges
dominate those of the process.
.NH 3
File descriptor files
.PP
The files
.CW /dev/fd/* ,
.CW /dev/tty ,
.CW /dev/stdin ,
etc, when referred to by file descriptor,
share labels with the file descriptors they
correspond to.
When referred to by name, these files have the
constant label $YES$.
.NH 3
Blind directories
.XL yb
.LP
A new file mode
.CW S_IBLIND
designates a directory as ``blind.''
No process can read a blind directory.
Only the owner of a file can remove it from a blind directory.
Only trusted processes can change blind mode.
.SP
Blind mode is special pleading to preserve the semantics of
.CW /tmp 
\- a compromise for compatibility. 
It affords an 80bps covert channel: High creates a file from some 
prearranged alphabet of names and Low tests whether the 
names are there.
(Bit rates are explained at \*(yY.)
.SP
Trusted processes that place temporaries of known name in
.CW /tmp
(or anywhere else) must take care to prevent improper
access by other processes.
For example, an untrusted user of a trusted process that
wrote and later reopened a file might replace the
file in the interim.
.NH 3
Log files
.XL yL
.PP
Security audit records are written to special files,
.CW /dev/log* .
Data so written are actually appended to an
associated ``repository'' file
nominated by the privileged
.I syslog
system call, \*(yl.
A log file 
can be written by any process regardless of label and can be
read by no process.
Identifying information is automatically attached to
each record written, so data cannot be forged and
histories can be reconstructed.
Direct writes on a repository file are silently discarded as on
.CW /dev/null .
Repository files are protected by normal access control.
.PP
A distinguished log file,
.CW /dev/log00
receives mandatory audit records in addition to data 
volunteered by writes.
The intensity of mandatory auditing is controlled in each process by
an audit mask, which is inherited across
.I fork
and
.I exec ;
see \*(yl.
.PP
Every file $f$ has a poison class $PC(f)$.
The poison class is normally invisible; it can be set or
interrogated only with capability $Tlog$.
At each system call that mentions file
$f$ in a pathname, the poison mask
$PM[PC(f)]$ is OR-ed into the process audit mask $AM(p)$.
.SP
Data written on log files escape
the formal policy.
Unlimited covert channels via writing on a log file and
reading from its repository are possible.
A prudent administrative countermeasure is to nominate
as repositories only files labeled $top$ or $NO$; external
media make particularly safe repositories.
.SP
Poison classes allow administrators to stipulate
extra logging when particular files are touched.
Thus sensitive activities can be watched carefully
without incurring a flood of low-value audit data
from routine activities.
.NH 2
Security behavior of old system calls
.XL yY
.SP
Bit-per-second (bps) estimates of covert channel
bandwidth given below pertain to
the research Tenth Edition (v10) running on a DEC VAX-11/750.
The bandwidth could be held to the same level on faster machines
by inserting a delay of about 100ms when
.I exec
is invoked with no arguments.
.SP
Many reasonable estimates can be made with only a few basic constants
and measurements:
the open file limit (128 in v10), the number of
forks a process can do per second (about 10),
the number of files a process can create or open per
second (about 80), and the number of message
round trips per second possible across a pair of pipes (about 80).
Some covert channels presuppose a population of files
readily identifiable from content.
We take 1000 as a population estimate, on the premise that
a much larger population would call attention to itself.
.SP
In making measurements some care must be taken to achieve
fast process-switching rates.
For example, in v10 it often helps to insert
.I nap
calls instead of busy-waits.
In general it is real time, not user and system time, that counts.
Also, because of overlaps, time measurements $x$ and $y$
often combine as $max ( x,^y)$ rather than as $x~+~y$.
.SP
Covert channels often need synchronization:
Low tells High, ``I got it,'' and then High sends another message.
The rules allow Low to pipe to High, which make this easy.
.NH 3
acct(f)
.XL z4
.LP
The writing of shell accounting records is immune to label checks.
.SP
Only a trusted process can nominate the accounting file.
It must assure that the file is appropriately
protected, perhaps by label $top$, by label $NO$, or by
write-only transmission off line.
Otherwise we have a 1000bps channel: High renames an executable
file and executes it; Low reads the name from the accounting
file.
.NH 3
chmod(f, m), fchmod(d, m)
.XL zm
.LP
There are two new
.I modes,
append-only,
.CW S_IAPPEND ,
and blind 
.CW S_IBLIND ,
the latter being useful only with directories.
.LP
If $f$ (or $f(d)$) is a directory and
if blind mode is changed and $not Textern (p)$ then error $EPRIV$.
.SP
If unprivileged processes could change blind mode,
High could create files in a virgin blind directory.
Later, Low could turn blind mode off and read the names.
.NH 3
chown(f, u, g), fchown(d, u, g)
.XL zn
.LP
If $f$ has setuid or setgid permission (mode 04000 or 02000)
and either the new userid or the new groupid differs from the
old then error $EPERM$.
.LP
If userid of $p$ is not superuser
.IP
If userid of $p$ does not own $f$ then $error$ $EPERM$.
.IP
If the new userid is not the same as the old then $error$ $EPERM$.
.IP
If the new groupid is neither the same as the old nor the same
as the effective groupid of the process then $error$ $EPERM$.
.SP
These rules have little bearing on the security policy.
However, by using
.I chmod
or
.I chown,
the superuser can circumvent discretionary denial of
write permission.
This gambit should be highly visible in an audit trail.
.NH 3
close(d)
.XL z7
.LP
If $d$ refers to a stream perform
.CW "ioctl(d, FIONPX, 0)" ,
but do not wait; see \*(yn.
.LP
If the file has been read and
$L(p)~<=~L(f)$, update the file access time.
.SP
To avoid the elaboration of a safe-to-write-access-time bit
in every file descriptor,
access times are not continually updated.
The access-time check reduces a covert channel:
High reads a file; Low spots the access.
The bandwidth is limited, by the rate at which files can
be opened, to about 50bps;
the check is a cheap frill.
.SP
A narrow (<100bps) covert channel using
.I close :
High selectively closes pipes, which Low detects with
.CW EPIPE .
.NH 3
creat(f, m)
.XL yf
.LP
If $f$ exists, perform only the RD part of the WRD (write directory)
check.
.LP
If $f$ is a log file (\*(yL) then error $EPERM$.
.LP
Otherwise, if $f$ is new
.IP
Set $L(f)~:=~bottom$.
.IP
Set $Vvec (f)~:=~0$.
.IP
Set $F(f)~:=~LOOSE$.
.IP
Perform the $W(f)$ check.
.LP
Otherwise
.IP
If the mode of $f$ includes
.CW S_IAPPEND ,
do not truncate $f$.
.IP
If the size of $f$ is nonzero, check $W(f)$.
.LP
Set $L(s)~:=~bottom$.
.LP
Clear the safe-to-read and safe-to-write bits
for the new file descriptor.
.SP
Notionally file labels and seek pointers begin at $bottom$.
However, 
.I creat
writes mode bits into a new file, so the label
rises immediately to $L(p)$.
.SP
The $W(f)$ check on non-empty files closes an 80bps covert
channel between unrelated processes: Low writes in a file;
High optionally truncates it; Low detects which.
In honest use, the label would probably rise anyway since
.I creat
is almost always followed by 
.I write.
.NH 3
dup($d$), dup2($d$,$d$)
.XL zd
.SP
Covert channel: High opens some files, uses
$dup2$ to selectively create more file descriptors, forks,
and $exec$s a low process.
Low infers which file descriptors are in use by attempting
to read from them, thus learning one bit of information
from the presence or absence of each possible file descriptor.
If High picks among files that have read permission,
no read permission, or a high label, the four possible outcomes for
each file descriptor yield about 250bps.
A similar bandwidth can be achieved by High picking among
a vocabulary of files which Low distinguishes by reading
inode number or permission bits with $fstat$.
.NH 3
exec(f, arg, env), umask(m)
.XL y6
.XL zu
.LP
This description pertains to all flavors of
.I exec .
.LP
Let $p$ be the executor process and let $q$ be the new process.
.LP
If $arg$ and $env$ are empty and no file descriptors have
numbers greater than 3
.IP
Set $L(q)~:=~bottom$.
.IP
If $L(p)~!=~bottom$ set $umask~:=~022$.
.LP
Otherwise set $L(q)~:=~L(p)$.
.LP
Perform the R($f$) check (\*(zr) in $q$, disregarding $Tnochk (q)$
and $F(q)$.
.LP
Clear all safe-to-read and safe-to-write bits in $q$.
.LP
Set all exempt bits in $q$.
.LP
Set $F(q)~:=~false$.
.LP
Set process licenses.
.IP
If $T(f)$ set $Uvec (q)~:=~Uvec (p)$.
.IP
Otherwise, set $Uvec (q)~:=~0$.
.IP
Set $Tvec (q)$ per \*(yQ.
.LP
Set $C(q)~:=~C(p)$.
.LP
Set $AM(q)~:=~AM(p)~or~SAM$.
.LP
Set $L(C(q))~:=~L(C(p))$.
.LP
Check R($f$) in process $q$.
.SP
The pex state (\*(y3) of open files persists across
.I exec.
.SP
It is understood that no data will pass across
.I exec
in registers.
This undocumented channel appears in some versions of
.UX ,
including v10.
.SP
Various covert channels arise from the
``drop-on-exec'' feature, which gives a bottom label
to a process when there is no memory via arguments.
These channels involve inferring the values of
freely settable uarea information:
open files (identifiable by inode number or content),
current directory (by inode number), program text file (by content).
To keep the bandwidth down, drop-on-exec
pertains only to processes that have at most the four
default file descriptors (standard input, standard output,
standard error, and control stream) open.
.RS
.SP
(1) Parent High opens three low files before
.I fork
and
.I exec ;
child Low identifies them by 
.I fstat
and writes the results in the fourth open file (230bps).
This is the widest known covert channel.
.br
(2) High executes a sequence of low files, which
record the sequence (100bps).
.br
(3) Parent High sets current directory;
child Low records it (80bps).
.RE
.SP
The permission mask,
being directly writable and readable by
.I umask,
could provide a direct channel on drop-on-exec.
That channel is closed by censoring the mask to a fixed value.
.SP
Licenses are inheritable only by trusted code.
If untrusted code could inherit licenses, it could
do nothing bad directly, but it could pass the licenses
to trusted code along with bogus arguments.
Then all trusted code would have to contain defenses against
the possibility.
.NH 3
fmount(n, d, f, m), fmount5(n, d, f, m, Cp)
.XL z8
.LP
Let $FS$ be $FS(f(d))$.
.LP
If the system call is 
.I fmount,
set $C(FS)$ and $Vvec (FS)$ to their
default values.
The default ceiling $C(FS)$ is $top$ on all file system types
except network file systems, where it is $bottom$.
The default privilege mask $Vvec (FS)$ is all zeros.
.LP
If the sysem call is
.I fmount5,
set $C(FS)$ and $Vvec (FS)$ from the
values pointed to by $Cp$.
.NH 3
getpgrp(q), setpgrp(q, n)
.XL yj
.LP
If $q~=~0$ set $q~:=~p$.
.LP
Otherwise, if $q~!=~p$ then $error$.
.LP
If the operation is
.I setpgrp
and $n~!=~p$ and $not Tuarea(p)$ then $error$.
.SP
The call
.CW "setpgrp(p, p)
(or
.CW "setpgrp(0, p)" )
is similar to the
.CW TIOCSETPGRP
.I ioctl
call; and is quite common in practice.
The requirement for privilege to set $n$ arbitrarily avoids
untrusted data flow through the process group.
.SP
The ability to set the process group on another process
would necessitate special label checks.  As that ability
is not used in v10 software it was deemed not
worth the trouble.  (``Job control'' shells, which
use the ability, have never caught on in v10 \- partly because
windows largely subsume job control.)
.NH 3
lseek(d, o, n)
.XL yh
.LP
This call is equivalent to, and checked like,
.I "seek(d, o, n)"
followed by
.I tell(d) ;
see \*(yH and \*(yI.
.SP
Seek pointers must be protected to stop a wide channel:
High sets a shared seek pointer; Low reads it (3000bps for
one file, proportionately more with many shared pointers).
If a seek pointer had the same label as its file
it would be impossible to read strictly up because the requirements
$L(f)~<=~L(p)~=~L(s)$ (\fIread\fR changes the seek pointer)
would degenerate to $L(f)~=~L(p)$.
Hence seek pointers have separate labels.
.NH 3
mkdir(f, m), mknod(f, m, a)
.XL yM
.LP
Set $L(f)~:=~bottom$, except where specified
differently for special files, \*(y1.
.LP
Set $Vvec (f)~:=~0$.
.LP
Set $F(f)~=~LOOSE$.
.LP
Perform the $W(f)$ check.
.LP
If the operation is 
.I mknod,
set the groupid of $f$ to be the same as the groupid of the
containing directory; if this differs from the groupid
of $p$ delete setgid from the mode of $f$.
.SP
Notionally file labels begin at $bottom$.
However, mode bits are written into a new file, so the label
rises immediately to $L(p)$.
.SP
A blind directory (\*(yb) cannot be created directly, because
.I mkdir
heeds only the 9 file permission bits in $m$.
.NH 3
nice(n)
.XL z2
.SP
In some systems (not v10)
.I nice
returns a value and could be used as a covert channel,
at least by the superuser.
.NH 3
read(d, b, n); dirread(d, b, n)
.XL yR
.LP
If $d$ refers to a blind directory, then $error$.
.NH 3
seek(d, o, n)
.XL yH
.LP
This call is like
.I lseek,
but returns an integer, \-1 for failure and 0 for success.
.LP
If $n~!=~1$ or $o~!=~0$ check $P(f(d))$, \*(yx.
.LP
Let file descriptor $d$ name $(p,^s,^f)$.
.LP
If $n~=~0$ check WS($d$) (\*(zs) as if $L(s)$ were equal to $bottom$.
.LP
If $n~=~1$ check WS($d$).
.LP
If $n~=~2$ check WS($d$) as if $L(s)$ were equal to $L(f)$.
.SP
Do not disturb $d$
if some process other than  $p$ has process-exclusive access to it.
On seeking relative to the beginning, the previous state
of $s$ is forgotten, so its previous label is irrelevant.
On seeking to the end, the new value of $s$ depends on 
the size of $f$, but not on the old value of $s$.
.SP
This function, resurrected from earlier
.UX
systems, avoids unnecessary label inflation that
could happen with 
.I lseek ;
see \*(yh.
.SP
An untrusted process that shares an open file with a
trusted process may by moving the seek pointer be able to insert
information into trusted writes.
In this way a process could influence downward
writes or writes above its ceiling.
To be safe, trusted processes should assert exclusive
access over possibly shared file descriptors; 
see \*(yX.
.NH 3
select(n, rd, wd, t)
.XL yA
.LP
Delete any descriptor $d$ such that
$X(f(d))~=~X(f sup prime (d))~=~PEXED$
and $H(f(d))~!=~p$ from the sets $rd$ and $wd$.
.LP
If $X(f sup prime (d))$ for some file
descriptor $d$ changes while waiting in
.I select,
report $d$ as ready; see \*(zP.
.SP
A file held in process-exclusive state by other processes is
not ready in $p$.
A file in an impure process-exclusive state may need attention.
.SP
Covert channels:
(1) High writes on one of several pipes; Low uses
.I select
to discover which; High empties the pipe by reading it.
(2) Low fills several pipes; High reads from one; Low uses
.I select 
to discover which.
.NH 3
signal(s, fp), kill(p, s)
.XL ye
.LP
Let $L$ be the label of the signal source.
.LP
If a signal would be caught and $L~!<=~L(p)$
then the signal is ignored.
.LP
If a core image is required, it will be made as if by
.I creat 
and
.I write.
However, if $Tnochk (p)$ was ever true, no core image
will be made.
The condition, ``$Tnochk (p)$ was ever true,'' is inherited across
.I fork
but not across
.I exec.
.SP
Downward signals provide a covert channel of only 100bps.
Stopping them is technically difficult, so we have not done so
in our experimental system.
.SP
A process with capability
$Tnocheck$
is trusted, and hence can be counted on not to spill its
secrets across 
.I exec
even if it relinquishes trustedness.
.NH 3
stime(t), biasclock(m)
.XL yB
.SP
By diddling the clock, a superuser can communicate
to an unrelated process.
The channel is highly exposed; superusers
have better ways to cheat.
.NH 3
tell(d)
.XL yI
.LP
Return, as a long, the current value of the seek pointer.
.SP
This call is resurrected from earlier
.UX
systems; see \*(yH.
.NH 3
vswapon(f)
.XL yK
.LP
Legitimate values of $f$, which are built into v10, must
be confined to nonremovable media.
.SP
No privilege has been required for this system call that
sets the swap device, because suitable devices are
automatically labeled $NO$.  Compile-time conventions
have been relied on to prevent an untrusted superuser
program from diverting swaps to a removable device.
.NH 3
times(b), vtimes(b)
.XL yC
.SP
A fraction of a bit per second may be communicated from
child to parent through
.I times ,
around 10bps through
.I vtimes.
.NH 3
unlink(f), rmdir(f)
.XL yu
.LP
If $T(f)$ then $error$.
.LP
If $f$ is in a blind directory and
userid of $p$ is not the owner of $f$ then $error$.
.LP
If $not Tnochk (p)$ and $L(f)~!<=~C(p)$ then $error$.
.SP
No process, not a even trusted process, may unlink
a trusted file.
To deter spoofing by file substitution in
.CW /tmp ,
only a file's owner may delete it from 
a blind directory.
A process may not delete files that it can't see data in.
.SP
Covert channel: Low creates a bunch of files and places links
to them in the blind directory
.CW /tmp .
High unlinks them selectively; Low detects the change
in link count and replenishes the links.
.SP
A $W$ check on unlink would narrow the covert channel.
But a $W$ check would have a nasty side effect:
innocently created files could get stuck so no
combination of untrusted processes could remove them.
For example, suppose Low creates, and freezes the label of,
a file in
a directory that High subsequently raises above Low's ceiling.
Low cannot remove the file because it can't see the directory.
High, or any other process that can see the directory,
will fail a W check because the file's label is frozen.
Only Low, the file's owner, can loosen the label.
The directory can't be deleted because the file is in it.
Both file and directory are stuck.
(High might get help from the superuser in unfreezing
the file, but there is no guarantee that an
unprivileged superuser can see all files that High can.)
.NH 3
wait(b), exit(s)
.XL yg
.LP
Let $q$ be the exiting process.
.LP
If either the exit or the termination code of $q$ is nonzero
and $L(q)~!<=~L(p)$, 
the status reported by 
.I wait
shows exit code 0 and termination code
.CW SIGTERM .
.SP
The status is censored to prevent downward data flow;
a 10bps covert channel remains.
.NH 3
write(d, b, n)
.XL yW
.LP
If the file mode of $d$ includes 
.CW S_IAPPEND ,
write at the end of file, regardless of the seek pointer.
Increment the seek pointer by the number of bytes written.
.LP
If $f(d)$ is nominated as a log file (\*(yl) then error $ECONC$.
.NH 2
New system calls.
.XL yN
.NH 3
fmount5(n, d, f, m, Cp)
.LP
See \*(z8.
.NH 3
getflab(f, Lp), fgetflab(d, Lp)
.XL zG
.LP
These two system calls return the label on a file, specified either by
file name or file descriptor.
The label $L(f)$ and privileges $Vvec (f)$ are placed in the
location $Lp$ points to.
.NH 3
getplab(Lp, Cp)
.XL zE
.LP
Return the label, ceiling and privilege vector of
the current process.
.LP
If pointer $Cp$ is not zero
.IP
Check RS($C(p)$).
.IP
If the check succeeds, place $C(p)$ and a zero
privilege vector in the location pointed to.
.IP
Otherwise place $NO$ in the location pointed to.
.LP
If pointer $Lp$ is not zero, place
$L(p)$ and $Vvec (p)$ in the location pointed to.
.LP
If the RS($C(p)$) check failed then $error$.
.SP
The system calls
.I setplab
and
.I getplab
mediate data flow through the ceiling label.
To enforce the formal policy on this flow the ceiling
label itself is labeled.  In the absence of such enforcement,
5000bps could be passed downward on this channel.
.NH 3
labmount(d, Cp)
.XL zl
.LP
Return the ceiling of the file system in which file
descriptor $d$ resides.
.LP
If $f(d)$ is in a file system place 
$C(FS(f(d)))$ in the location $Cp$ points to.
.LP
Otherwise place $YES$ in the location pointed to.
.NH 3
nochk(fd, code)
.XL zk
.LP
If $code~=~0$
mark file descriptor $fd$ not exempt and clear
safe-to-read and safe-to-write bits in $fd$.
.LP
Otherwise, mark $fd$ exempt.
.LP
Return 0 or 1 according as $fd$ was not or was exempt before.
.SP
Exempt bits are turned on by default (\*(y6), although
the opposite convention would be better.
The present convention allows some administrative programs
that need $Tnochk$ privilege to be identical
with those on ordinary
.UX
systems.
.NH 3
setflab(f, Lp), fsetflab(d, Lp)
.XL y2
.LP
These two system calls set the label on a file.
The description applies to
.I setflab ;
.I fsetflab
is to
.I setflab
as
.I fchmod
is to
.I chmod.
The proposed new privilege vector $Vvec$, fixity
$F$, and label $L$ are pointed to by $Lp$.
.LP
If userid of $p$ is not superuser or owner of $f$ then $error$
.CW EPERM .
.LP
If $f$ is a process file then $error$.
.SP
Prevent violations of
the ceiling or increases in privilege of the process.
.LP
Check privilege:
.IP
If $Tsetpriv (p)$ the check succeeds.
.IP
Otherwise, if $F~!=~F(f)$ and userid of $p$
is not the superuser or the same as the owner of $f$, then error
$EPERM$.
.IP
Otherwise, if $T(f)$ then $error$.
.IP
Otherwise, if $Vvec$ is nonzero then $error$.
.IP
Otherwise, the check succeeds.
.LP
If the privilege check succeeds, check labels: the following label check:
.IP
If $L ~=~ YES$ 
then $error$.
.IP
Otherwise, if $L ~=~ NO$
.RS
.IP
If $L(f)~<=~C(p)$ the check succeeds.
.IP
Otherwise $error$.
.RE
.IP
Otherwise, if $L(f)~=~NO$ and $Textern (p)$ the check succeeds.
.IP
Otherwise, if $L(f) ~!<=~ L$ then $error$.
.IP
Otherwise, if $Tnochk (p)$ the check succeeds.
.IP
Otherwise, if $L(p)~<=~L~<=~C(p)$ the check succeeds.
.IP
Otherwise $error$.
.LP
If the label check succeeds, check fixity: the following fixity check:
.IP
If $F(f)~=~CONST$ then $error$.
.IP
Otherwise, if $F~=~CONST$ then $error$.
.IP
Otherwise, if $F~=~RIGID$ and $f$ is not a stream then $error$.
.IP
Otherwise, if $F(f)~=~LOOSE$ the check succeeds.
.IP
Otherwise, if $F(f)~=~FROZEN$ and userid of $p$ is the same
as the owner of $f$, the check succeeds.
.IP
Otherwise, if $F(f)~=~RIGID$ and $Textern (p)$
the check succeeds.
.IP
Otherwise $error$.
.LP
If the fixity check succeeds, change the label:
.IP
If $F(f)~=~RIGID$ then set $F~:=~RIGID$.
.IP
If $L(f) ~!= ~L$,
and if $f$ is an external medium, then
block all input/output activity for all openings of $f$,
drain its output buffers, and flush its input buffers.
.IP
Set $F(f)~:=~F$.
.IP
If $L(f) ~!= ~L$ or $Vvec (f)~!=~Vvec$, then set $L(f) ~:=~ L$
and $Vvec (f) ~:=~Vvec $
and propagate with CHF($f$); see \*(yF.
.IP
Unblock input/output (if blocked).
.SP
Processes can be marked trusted only with trusted tools.
Trustedness also can be removed only by trusted tools, not
to forestall security breaches, but to preserve the
tools themselves.
.SP
Labels can only go up.
Trusted processes can upgrade anything.
Trusted processes may downgrade only by copying.
The labels of external are rigid; their labels can change only with
capability $Textern$; see \*(yD.
.SP
.I Setflab
on a file labeled $NO$, usually an external medium, renews the file
(\*(y5).
A file may be downgraded
by setting its label to $NO$ and then using capability $Textern$
to set the label away from $NO$.
.NH 3
setplab(Lp, Cp)
.XL yd
.LP
This system call sets the label, privilege,
and ceiling of the current process.
The proposed label $L$, privilege $Vvec$, capability
$Tvec$, license $Uvec$ and fixity $F$ are pointed to by $Lp$;
the proposed ceiling by $Cp$.
A zero pointer designates a proposed value equal
to the current value.
.LP
Check privilege:
.IP
If $Tvec$ is not bitwise
less than or equal to $Tvec (p)$ then $error$.
.IP
Otherwise, if $Uvec$ is bitwise less than or equal to
$Uvec (p)$ the check succeeds.
.IP
Otherwise, if $Tsetlic (p)$ the check succeeds.
.IP
Otherwise $error$.
.LP
If the privilege check succeeds,
check labels:
.IP
If $L ~=~ YES$ or
if $L ~=~ NO$ or
if $C ~=~ YES$ or
if $C ~=~ NO$ then $error$.
.IP
Otherwise, if $L~!<=~C$ then $error$.
.IP
Otherwise, if $L(p)~!<=~L$ and $not Tsetlic (p)$ then $error$.
.IP
Otherwise, if $C~!<=~C(p)$ and $not Tsetlic (p)$ then $error$.
.IP
Otherwise the check succeeds.
.LP
If the label check succeeds then
.IP
If $F~=~RIGID$ or $F~=~CONST$ then $error$.
.IP
Set $F(p)~:=~F$.
.IP
If $L(p)~!=~L$ set $L(p)~:=~L$ and propagate with $CHP()$;
see \*(yP.
.IP
If the process loses capability $Tnochk$ or $C(p)~!<=~C$
then clear all safe-to-read and safe-to-write bits in $p$.
.IP
Set $Vvec (p) ~:=~ Vvec$.
.IP
Set $C(p)~:=~C$.
.IP
If $Cp$ is not zero set $L(C(p))$.
.RS
.IP
If $not Tsetlic (p)$ set $L(C(p))~:=~L(p)$.
.IP
Otherwise, set $L(C(p))~:=~bottom$.
.RE
.SP
The label of an untrusted process can only go up;
the ceiling can only come down.
Label and ceiling may never cross.
If the ceiling passes the label of open files, subsequent
R($f$) checks (\*(zr) or W($f$) checks (\*(zw) will fail.
.SP
.I Setplab
does not observe 
the license $Tvec (f)$ of the file being executed
or the maximum license $Uvec sup 0$; these are used only
for initializing after
.I exec.
.SP
The bits in the ceiling are labeled (by $L(C(p))^$) because
the ceiling is readable and to some extent writable
by an untrusted process.
If the ceiling were not labeled,
a lowish process with an all-ones ceiling could
leak more than 5000bps by twiddling the ceiling.
.NH 3
syslog(c, n, x)
.XL yl
.PP
Change or inquire about security auditing.
Argument $n$ is an integer, which may also be interpreted as
a file descriptor, $d$, or as a process id, $q$.
.LP
Switch on $c$ into
.IP
Case $font CW LOGON$: nominate file $f(d)$
as the repository for the log file with minor device number $x$, \*(yL.
.IP
Case $font CW LOGOFF$: turn off logging on device $x$.
.IP
Case $font CW LOGGET$: return poison mask $PM[n]$.
$PM[4]$ means the system audit mask $SAM$.
.IP
Case $font CW LOGSET$: set $PM[n]~:=~x$.
$PM[4]$ means $SAM$.
.IP
Case $font CW LOGFGET$: return poison class $PC(f(d))$.
.IP
Case $font CW LOGFSET$: set $PC(f(d))~:=~x$.
.IP
Case $font CW LOGPGET$: return process audit mask $AM(q)$.
.IP
Case $font CW LOGPSET$: set $AM(q)~:=~x$.
.LP
Each bit of an audit mask designates a
class of mandatory audit records.
The classes are
.EQ
delim off
.EN
.de ZP
.IP \f(CW\\$1\\fP
..
.EQ
delim $$
.EN
.br
.RS
.ZP N
uses of file names (calls to
.I namei
in the kernel)
.nr PD 0
.ZP S
seek calls
.ZP U
writes to the ``uarea''
.ZP I
accesses of inode contents:
.I stat (2),
.I utime (2),
etc.
.ZP D
possession and use of file descriptors:
.I open (2),
.I close (2),
.I read (2),
.I write (2),
etc.
.ZP P
process history:
.I exec (2),
.I fork (2),
.I kill (2),
.I exit (2)
.ZP L
explicit changes of labels:
.I setflab (2),
.I setplab (2)
.ZP A
all changes of labels
.ZP X
uses of privilege
.ZP E
$ELAB$ error returns
.ZP T
uses of a traced file or process
.RE
.nr PD .3v
.LP
The format of audit records varies with the kind
of action recorded.
.SP
Writing of audit records is immune to label checking; the only
security check is that the process which sets
.CW LOGON
is trusted and has been able to open file $d$.
Whether $d$ is open for reading or writing does not matter.
Logging persists after file $d$ is closed.
Log files may share repositories.
.SP
It is the duty of the trusted nominating process to
assure that the repository is protected so
that loggging records cannot be read in violation
of the security policy, \*(yL.
.NH 3
unsafe(n, rp, wp)
.XL y8
.LP
This system call queries and selectively clears
safe-to-read and safe-to-write bits.
.LP
Two bit strings, $rs$ and $ws$,
pointed to by $rp$ and $wp$ are indexed as in
.I select.
Let the actual safe-to-read bits and safe-to-write
bits of all file descriptors constitute strings $rd$ and $wd$.
Only the first
.I n
bits of each string are considered.
.LP
Do simultaneously
.IP
Set $rs~:=~rd$ and $ws~:=~wd$.
.IP
If $Tnochk (p)$ then set $rd~:=~rd & not rs$ and $wd~:=~wd & not ws$.
.SP
Covert channel: High has pipe to Low; High raises level; Low uses
.I unsafe
to discover it.
The channel runs dry when High hits its ceiling, so not many
bits\(emprobably less than 20\(emcan be transmitted.
The call might be restricted to trusted processes, which are
expected to be its principal user; see \*(yS.