OS security

Required reading: HiStar.

Unix security

why security in the OS?
    managing resources for different applications
    must protect different users from one another
	file system

Unix model
    designed for specific purpose: multiple users time-sharing a Unix system
    users identified by a user id (uid, usually 32-bit today)
    uid 0, called root, treated specially by the kernel as administrator
    each process has a uid, which is used in many security checks
	file system:
	    each inode (file, dir) has an owner uid (and group; ignore for now)
	    basic operations: read, write, execute [rwx]
	    inode has 3-bit permissions for owner, group, and everyone else
	    directory operations also map to rwx perm checks
	uid 0 has extra privileges
	    can change uid, create device inodes, chown, reboot, etc
    some security mechanisms, however, aren't based on uid's
	file descriptor access
	chroot mechanism (only root can chroot, to not confuse privileged code)
	wait(), getting the exit status of a process
    how do these uid's get set?
	setuid() call
	uid=0 can change to any other uid
	other uid's cannot invoke setuid(), to a first approximation
    Unix login
	runs as root
	checks username, password against /etc/shadow
	calls setuid(user), runs user's shell
    not a great model for login: way too much privileged code
	rlogind runs "login [-f] username", -f if already authenticated
	user asks to log in as user "-froot", avoids password check, gets root
	neither should have been running as root in the first place?
	how else could you do login?  KeyKOS receptionist.  or HiStar, later.

setuid programs
    what's missing in our model?  no way to get back to higher privilege.
	how would we implement the "su" command?
    setuid programs fix that, by coupling privileges to a binary or exec()
	special "setuid" bit in the permissions of a file
	on exec() of binary, kernel sets process uid = file uid
	often, file owned by root, so process will run as root
    how to tell who called you?
	kernel keeps a real + effective UID for each process
	setuid binaries change effective UID, keep real UID
	effective UID used by most syscalls
	some syscalls explicitly use real UID (e.g. access)
	allows explicitly checking permissions "as if" it was the caller
    why might this be a bad idea?
	from earlier lectures: confused deputy, TOCTTOU (with symlinks)
	binary inherits process from "malicious" user
	environment variables: LD_PRELOAD, PATH
	    fix: libraries ignore LD_PRELOAD in setuid programs; use full path
	    secure setuid shell scripts hard to write; bash explicitly prohibits
	file descriptors: what if stdout was closed?  overwrite some file..
	    fix: kernel will open dummy fds for 0, 1, 2 if not already open
	sending signals: user wants to kill setuid programs they ran
	    kernel allows signals if uid matches
	    not quite right, so filter signums (e.g., no SIGALRM)
	ptrace: can debug your own procs, but not others
	    how does this interact with setuid programs?
	    used to be a bug in linux
    unfortunately, no other way to pass privileges in Unix, so setuid is used

limitations of Unix security
    can't pass privileges to other processes
    can't have multiple privileges at once
    not a very general mechanism (cannot create a user unless root)

    subjective policy (no objective underlying principle): unexpected results
	(used to be) easy to escape from chroot with uid=0
	    fd=open("/"), chroot("/tmp"), fchdir(fd), chroot("./../../../..")
	can create hard link to any file, even if we can't read or write it
	buggy code remains setuid even if root rm's it, installs fixed version
	keep a copy of sensitive data for later when you can obtain root access

Mandatory access control

historically, military was interested in a different security model
    what if the enemy gains access to secret data?  could email it somewhere.
    mandatory access control vs discretionary
    Unix is discretionary, because process can specify security policy
	change permissions on files it writes to, give its privileges to others
    military goal was to disallow such discretion
	mandatory policy set by security officer, processes have no choice

typical model: Bell-LaPadula, concerned with secrecy
    system has subjects (e.g. processes) and objects (e.g. files)
    each subject and object is assigned a security level (c,s)
	c = classification (unclassified, secret, top-secret, ...)
	s = category set (nuclear, crypto, NATO, ...)
    "dominates" relation:
	(c1, s1) dominates (c2, s2) iff c2 <= c1 and s2 \subseteq s1
    use the dominates relation for access control
	S does an operation on O
	if operation will allow S to observe O, check if L(S) dominates L(O)
	if operation will allow S to modify O, check if L(O) dominates L(S)
    the dominates relation is transitive, so we get nice properties
	malicious process cannot copy top-secret file to unclassified file

can we implement this on Unix?
    suppose we associate a security level with each file, directory and process
	when user logs in, shell's process level starts low
	check "dominates" on all file access, pipe messages
	when reading a file, raise process level as needed (but never lower)
    does this work?
	turns out that it's not enough: lots of covert channels
	spawn a child with shared fd, child reads secret data, stores fd offset
	lots of other examples: proc table, memory use, fs link counts
	timing channels: secret proc affects timing of unclassified procs
	requires lots of work to retrofit this into Unix

flip side: Biba model, protects integrity
    want to ensure that network attackers can't modify system binaries
    turns out to be quite similar to Bell-LaPadula
	secrecy: no reading more secret files, writing less secret files
	integrity: no writing higher-integrity files,
		      reading lesser-integrity files
    add an extra integrity value to the security level

why didn't these ideas get widely used?
    1) very specialized security level scheme, fits military but not many others
    2) hard to figure out when to declassify data -- everything becomes secret
    3) unclear interaction with traditional access control (Unix users, groups)
	still need both mechanisms
    SELinux is getting some interest, maybe even some use


goal: OS abstractions to build more secure applications
    (1) make protection mechanism general, available to every process.
    (2) improve security by making parts of the application untrusted,
	by specifying security policies in terms of information flow.

why information flow?
    paper argues that some security policies better phrased this way
	not just military, but also virus scanners, VPNs, ...
    more generally, controlling information flow controls causality
	can potentially control how different untrusted components interact

how to track information flow?
    tracking fine-grained dependencies along with every bit is hard
    instead, label objects that contain data (threads, files)
	be conservative, assume the "worst" for data coming from an object
    what's the representation of information flow?
	lots of categories of data (my email, my ssh key, another user's mail)
	    anyone can create one of these categories
	roughly, subject and object security labels are a set of categories
	label model captures both Bell-LaPadula and Biba
	    object and subject labels actually map categories to (0,1,2,3,*)
	    "1" is effectively "no restriction"
	    "3" is secrecy-protection in a category
	    "0" is integrity-protection in a category
	    label syntax: { c1 l1, c2 l2, ldef }, ldef usually 1
	equivalent of the dominates relation:
	    L1 can-flow-to L2 iff, forall c, L1(c) >= L2(c)
    examples of labels that can-flow-to or cannot-flow-to
	{ mymail 3, 1 } cannot flow to { 1 }
	{ mymail 3, 1 } can flow to { mymail 3, mykey 3, 1 }
	{ mymail 3, 1 } cannot flow to { otheruser 3, 1 }
	{ 1 } cannot flow to { systembin 0, 1 }

ownership: how to declassify?
    centralized security officer doing declassification isn't going to work
    decentralize privileges
	whoever creates a category gets to "own" it (special level *)
	owner can ignore restrictions in that category, thereby declassifying
	can grant ownership to other threads
    allows precise statement of system's security goal (paper, p4):
	The contents of object A can only affect object B if,
	for every category c in which A is more tainted than B,
	a thread owning c takes part in the process.
    example: virus scanner
	security goal translates into wrap intercepting all output
    still mandatory security, except that we've decentralized "policy-setting"

how do unix fs permissions map to labels?
    world-readable, world-writable: { 1 }
    world-readable, owner-writable: { u_w 0, 1 }	  (u_w for user owner)
    owner-readable, owner-writable: { u_r 3, u_w 0, 1 }   (u_r for user_owner)
    can we do groups?  sort-of, except user owner has to be in group
    fs similar to keykos: separate kernel-protected object for each inode

how is this better than the military Unix systems from the 80s?
    don't need separate Unix security in addition to inf. flow
    kernel has few object types & syscalls
	clear when information flows, and it's the only security mechanism
	objects: threads, segments, containers, address spaces, gates, devices

HiStar, part 2

what does the kernel interface look like?
    objects named by 61-bit IDs
    each object has a label for protection
	segments: sequence of pages
	threads: register set and the object ID of an address space
	address space: table of VA -> segment object ID
	.. and some others that aren't important for now
    basic plan: few system calls, always clear where information is flowing

label recap:
    objects have a label that tracks secrecy, integrity in different categories
    data can generally flow to higher secrecy and lesser integrity
    threads can own categories and bypass these restrictions
    threads also have a clearance: upper limit for labels of allocated objects
	raising thread's own label is a degenerate case of object allocation
	also prevents malicious tainted thread from stashing copies of secrets

what does a process look like?
    need a thread, a segment, and an address space
    simple protection: segment, AS only accessible to process threads
    we'll build up as we go along

how do we implement a FS?
    goal: minimal trusted code
    similar trick to KeyKOS: full persistence in the kernel
	allows a lot of FS code to run in user-space, not in trusted kernel
    each file is a segment
	where does mtime go?  64 bytes of metadata with every object.
    each directory has a segment that maps names to object IDs
	either files (segments) or sub-directories
    access control in terms of labels on files, directories
    how would a process access this FS?
	need a FS library that translates open, read, write into segment ops
	how to implement rename in a directory?  change the name in dir segment
	need to ensure atomicity: how to make sure two threads don't collide?
	    spinlock in shared memory (in directory segment itself)
	    in reality, spinlocks are bad.  how to go to sleep?
		kernel provides an addr-sleep mech, much like inside xv6 kern
		Linux calls this a futex
		slightly non-trivial: segment can be mapped at diff. VAs
		labels control who can sleep or wakeup an address
	    what about read-only directories?  generation# trick.
    how does this compare to the KeyKOS FS?
	can't change the file implementation quite so easily
	still one protection domain per file or directory

cool feature of labels: no inherent superuser privileges
    can run ssh-agent without giving root access to your keys
    but this seems potentially bad -- what to do with buggy ssh-agent?
	esp. with single-level store, this is a bit of a problem
    solution: need a resource allocation mechanism
	keykos had space banks and sub-banks
	histar has containers
	new kernel object: container (back to diagram!)
	what does this look like?  basically, rmdir non-empty directories.

file descriptors?
    segment for each FD, very JOS-like (keeps offset)
    mapped in address space of every process with access to it
    but anyone can map segment into AS, so use a per-fd category in labels
    what container does the FD live in?
	want to make sure FD is accessible even if other process exits
	kernel allows object hard links

object naming -- why <container ID, object ID>?
    what could go wrong if we just named objects by object-ID?
	secret proc could create hard links to different objects
	unclassified proc removes objs from container, tries to access
	success or failure indicates hard links in other containers
    problem: object's existence itself conveys information
    naming an object through a container requires observing container
    as a result, caller allowed to receive data from any container writer
    explicitly allowed to see whether object exists or not
    cannot name objects gone from our container (even if exists elsewhere)

process exit
    need a separate segment object to keep exit status value
	and actually a higher-level process container to expose it
    only the process should be able to write it, anyone can read
    what happens if you ran a process that looked at some secret files?
	in theory, shell should never hear from tainted process
	but really want to get back to shell prompt
	two possibilities:
	 - process reports it's about to become tainted, then taints itself.
	 - owner of tainted category allows process to leak one bit of data;
	   very explicit in user-space, rather than weakening kernel mechanism

how do signals work?  e.g. ^C
    base kernel operation: trap a thread, like utraps in JOS
	who can trap a thread?
	can trap a thread if can read/write thread object itself
	that's often too restrictive, so can trap with write access to AS
    but a different process can't access either thread or AS of another proc
	fundamental goal: need privileges of another process, for specific op
	new kernel object: gate, associates privileges with some code (diagram!)
    so how do gates work?
	gate call: thread changes AS (page table) & registers to those in gate
	can also change label, using privileges (stars) from gate's label
	[actually need more precise label manipulation, for tainted gates]
	not quite IPC because there doesn't need to be anyone waiting for call
	what's "left" of the thread?  resource allocation (container)
    signal gate for killing process: per-user category limits callers

how to name a process that we want to kill?
    unix: global proc_table
    keynix: still a global proc_table, to map PIDs to capability keys
    histar: use the container ID of a process container as the PID
	given a PID, always clear how to find proc, signal gate, exit segment

gates example app: timestamping service
    send secret msg to server, server can only reply to you
    do we need this gate thing?  can this work with pure IPC?
	need to make sure server can talk to other procs afterwards
	fork server for each message?  resource problem
	    alternative 1: infinite resource use for server forks.  bad.
	    alternative 2: bound the number of server forks.  covert channel.
    how are we going to use gates?
	invoke service with taint, what can the thread do?
	initially, can't write anywhere (except a special thread-local page)
	caller provides a writable, tainted container
	thread copies AS, segments (text, stack, data), etc into container
	switches to cloned (now writable) address space, uses key to sign
	cannot leak private data outside of its tainted container
	sending response back: invoke a return gate
    keykos analogy: key call, but that created a return key automatically
	why does histar gate call not create a return gate?
	if it's identical to a regular gate, no need to do it in kernel
	keykos return keys special (can only return once), can we do that?
	return-once semantics leak information, so histar deals in userspace
	    timestamping service could play same trick on the caller!
	    return at different taint levels, see how it behaves
	    then return for real at a low taint level
	    not great, what can we do?  refuse tainted return.
	    for untainted return, we can write to original AS, remove gate.

what applications would benefit from histar labels?
    wrap virus scanner
	user experience: wrap scan ~
	unix: hard to implement
	keykos: need to understand all capabilities wrap is giving to scan
	histar: just label scan with new category {v3}
	main thing: protection and naming separated
    VPN isolation
	what's the problem?
	    single machine, connected to internet and corporate VPN
	    viruses go into corporate network, secret documents leak out
	    should be able to use taint to prevent this
	how does the network stack work in histar?
	    very much like jos (lab6 code came from histar)
	    TCP stack not trusted, nothing particularly special about it
	    can easily run a new one for VPN
	    how does the application find the TCP stack?
		lab6: special env_id
		histar: /netd (object id for "netd" in root directory segment)
	figure 11 from paper
	could we do this in keykos?
	    hard: need to know ahead of time if process should be {i2} or {v2}
	what happens if the TCP stack is malicious?
	what happens if the VPN code is malicious?

how does login work in HiStar?
    one agent per user, checks password, grants user privileges (stars)
    so far, similar to KeyKOS.  but one diff: agent cannot leak pw.
    why is this hard?  also want agent to log authentication attempts.
	idea: instead of checking pw, create another agent to check it later
	no password involved -- can log that we created this second agent
	same trick to log a successful auth: grant an "intermediate" star
    new problem: second agent invoked many times to guess pw, cannot log!
    idea: retry counter, limits how many times second agent will work
    how to create?  turns out, neither party can create it on their own
	login process should not be trusted -- can create many of them
	auth agent should not be trusted either -- not as obvious
	    allowing it would involve login granting clearance to auth agent 
	    auth agent could place retry counter out of reach of login process
	    have second agent stash pw there, leak it through covert channels
	    login process loses control over copies of password
    solution: simple mechanism to combine privileges
	both parties know what a system call instruction looks like
	login process creates a gate pointing to create-segment syscall code
	auth agent checks that gate points to create-segment syscall code
	invokes gate with its own privileges
	only needed mechanism: read-only objects

	cool applications, could not do this in Unix or KeyKOS
	inf. flow seems useful: don't have to trust even password checker!
	kernel seems smaller, maybe fewer bugs, but nothing formal
	seems comparable to Linux with high-level benchmarks
	why is fork slow?  why is spawn better?