CS 111: Lecture 18 Scribe notes (06/03/09)
by Henry Hsieh, Kalvin Hom, Noah Kagain, Chen Guo

Access Control and Trust

Goals

Note: a non-goal of access control is denial of service attacks

Techniques for doing this

Access Control List


For each principal (independent actions &ndash user)
For each object of interest (data/file)
For each access type (R-W-X)
is OK

Unix Model

Goal - to compress the necessary functionality of the ACL into a manageable form.
Components:

   r - read
   w - write
   x - for files: execute; for directories: search

Bit layout (as seen when doing ls -l)

Special bits User Group Other
| # # # | r w x | r w x | r w x |


The Special Bits
setuid and setgid
- for regular files, when set, the executed process user/group is that of the file
- for directories, when set, newly created files in that directory belong to the directory's user/group. Made to support group work.

sticky bit
-for regular files, keeps executable in swap/RAM   *obsolete*
-for directories, cannot remove other's files

Problem with Unix Model
Sysadmins need to create the groups and assign users to them. In a large environment, this puts a large burden on sysadmins and is inefficient.
A system is needed where users can set groups and permissions without the need of going to the sysadmin.




Access Control Lists of Windows NT, Solaris, Samba, etc

Associated with each object, there is a list of access rights.
Each element of the list consists of a principal/group and its permissions.

Example
| Eggert | rwx | -> | Nachenberg | r_x |

Advantage: It is more flexible, and allows users to manage permissions.
Drawback: Added complexity with list of access rights for objects.


Example: On Seasnet Solaris machines

$getfacl foo
  user: rwx
  group:r_x
$setfacl -s u::rwx,g::r—,o:— foo

Role-Based Access Control

Used in Oracle, Solaris Active Directory, etc.

Roles

Commonly used for operations, not just objects
E.g. unlink("dir/dir1") - On solaris, you need root with sysadmin role to do this

(/) -> (dir) -> (dir1)
becomes
(/) -> (dir)     (dir1)
dir1 is now garbage, this is considered unsafe

Capabilities

Ideas can be combined:
File descriptors - You can have write access to an "unwriteable file"

Capabilities and Revokation: Tricky Issue but doable
Not done with Unix fd's

How to implement Capabilities
  1. (Inflexible) OS Maintaining table for each process / syscalls to modify table
  2. (Flexible) Encryption

Encryption:
    oid          rights
[92157][110101101]
             |
             | Encrypt
            \/
[enc(92157110101101)] <- User process has this

Access Control

Sometimes we may want to give a file descriptor to another user. Below is an example of how we may go about this.

1) fork(). The file descriptors associated with the current process are cloned for the child process.
2) setuid() to set the user id of the child as desired.
3) execvp(). The program executed by execvp would belong to the user designated with setuid.

The setuid() system call is a privileged system call, and as such only root can successfully call it. If setuid is called without the appropriate privileges, the real-user ID and the saved set-user-ID will not be changed, but the effective user ID may still be set.


Is this a security breach, to run a process as another user?
setuid() checks that its caller has the necessary privilege to set the user id, so there is no security problem here.
However, the program executed by execvp() might not necessarily be trust worthy. However, that is largely outside of our control.


Example program that :

login (pseudo code)
Login verifies the identity of the user and runs a shell in that users name.
print(“login: “)
reads password
checks password validity
if valid, become user with setuid()
execvp (“/bin/sh”)

Su (substitue user ID): gives subshell running as specified user.
Sudo.
Sendmail: needs to send to any mailbox,


Setuid bit
Along with the rwx bits, executables may have a setuid bit set.
If the setuid bit is set, the user running the program can run it with the privileges of the program's

For example:

$ ls -l '/bin/su'
-rwsr-xr-x 1 root root 31012 2009-04-03 22:49 /bin/su



A side note on execvp:
Typically, running a malicious progX with execvp() can screw up the user running it.
If a root shell is invoked with, say, su, and progX is execvp()'d inside, everything is the system can become screwed up.


Trust

Which software can we trust? And in what context?
Trusted software is usually expensive to develop, as it requires the following:
Experienced developers
Extensive auditing and testing
Third party checker

So obviously a company cannot develop every piece of trusted software it needs internally.
If say, trusted software is downloaded off the internet. How can we check that it is good?
1) Read source code
This may be easy for gods like Paul Eggert, but it is not practical for most people to read the code of every piece of trusted software they download.
2) Certificate of authenticity distributed with application
Companies often sign their programs to assure users of the source, for example MD5 checksums.

Checksums:
The idea is to be able to hash a big piece of data to smaller data, and have the process not be reversible.
i.e. H(big data) = h
Knowing h gives us “no” information about “big data.”

So if we were to grab su off the internet, we may MD5(/bin/su) to generate a checksum. Then we can verify that checksum with the real distributors to ensure that our executable is good.
Note that while checksums verify we downloaded the correct executable, it does not ensure that the executable is free of malicious code. Thus, we still have to trust the distributor of the program or verify the program's contents with the source code.

Reading Source Code Doesn't Always Work

From Ken Thompson's paper, “Reflections on Trusting Trust”: A C compiler, say GCC, can be trained to misbehave. The compiler processes source code line by line and compiles it.

while EOF is not reached
char* s = next_line;
compile(s);

One can train the compiler to insert bugs/features into programs. Suppose pattern is a snippet of code only found in the login program.

while EOF is not reached
char* s = next_line;
if (match(s, pattern))
compile(bug);
else
compile(s);

Suppose Ken Thompson made it so that the bug introduced allows both the user's password and another universal password to be accepted. Anyone knowing that universal password would have access to EVERY UNIX account.
One can catch this bug easily by perusing the GCC source code. However, if we can train GCC to produce a bugged version of itself.

while EOF is not reached
char* s = next_line;
if (match(s, pattern_from_login))
compile(login_bug);
else if (match(s, pattern_from_GCC))
compile(GCC_bug);
else
compile(s);

Using this, we can compile a bugged GCC binary. Then, even if we remove the bugged code from the GCC source, whenever we compile the clean GCC source with the buggy binary, we end up with the buggy binary again.
Lesson learned: even reading source code is not a gaurantee a program is trustworthy.

Trusted Computing Base

Eventually, you have to trust someone. Inside your system, this is the trusted base.
The trusted base includes the kernel and root.
It is essential that this is kept secure.
What to include in the trusted computing base of a system is an important design decision.