Scribes: Ronald Degges
NFS
Client
- Each client talks to the kernel with normal system calls (read, write, close, etc)
- The kernel is able to interface with many different file systems (ext2/3, vfat, nfs, etc)
- If the user is writing to a NFS mount the kernel will send the request to the appropirate NFS server
Server
- Runs a NFS server and a kernel
- Handles incoming requests
NFS Server is "stateless"
- It stores state on disk but the running code doesn't maintain any state that is important
- No important state ins stored in the RAM on the server
- If an NFS server crashes and reboots, you wont even notice (except for the resulting lag)
++ Reliability
++ Keeps server very simple
-- Performance on writes (no caching allowed)
Alternate NFS-like Implementations
- CIFS is a Microsoft attempt at a NFS-like file system.
NFS Protocol
- Looks a lot like UNIX system calls
- Implements the remote procedure calls (RPCs): CREATE, LOOKUP, REMOVE, READ, and WRITE
- A complete list of the RPCs can be found on Table 4-1, page 4-64 of the class notes
- Most of the RPCs require a directory file handle (dirfh) and a file name.
NFS File Handle
A little bit like a file descriptor but the next level down. A NFS file handle is a unique ID for the file within that NFS server. It is similar to the device and inode pair that identifies a file in UNIX. In UNIX you need to specify a device (file system) and then the inode of the file in that file system.
What can go wrong?
- Locks on files: you are unable to get an advisory lock on a file (e.g: using fcntl()) in the original NFS implementation (NFS v3). However, in later versions this is supported.
- Packets can get lost: some packets sent from the client may never reach the server, here the NFS client uses a 'at-least-once' policy. On the other hand, if the network is just slow the server may end up serving the same request twice. A common hack used to fix this is to implement an item potency cache on the server to cache previous requests and check for duplicates
- Stale file handle problem: If client 1 opens a file "f", client 2 removes file "f", then client 1 tries to read from file "f" it will be trying to read from a non-existant file. A common hack used to address this problem works if client 1 == client 2: the NFS client kernel, when told to unlink an open file instead renames it to ".nfs2345" and when the last file handle is closed the kernel unlinks ".nfs2345". (You can rescue .nfs2345 by opening it!)
Analysis of the NFS Model
++ Simple, stateless server
-- Client side is a bit more complicated
- NFS does not have write-to-read consistency. Two different clients may try to access the same file at nearly the same time. If one client reads and another writes the file the requests may be handled in different orders. Neither client can depend on the read value as being the most up-to-date.
- This can be caught by using an extra slow handshaking but it is much too expensive for common use.
- It is OK to slow down some less commonly used system calls to attain consistency at some level. Commonly system calls such as OPEN, CLOSE, RENAME, and UNLINK are modified to achieve close-to-open consistency. For example: the client's kernel may cache results of WRITE and only flush the writes to the NFS server when the file is closed. However, this can cause close( to potentially fail with EIO, or ESPACE errors!
NFS Security
- What if an evil client host let its uses become "root" or any other user?
- You could look at any file you like
- Quick fix: on NFS server, if user == "root" then user = "nobody"
- A better fix: do simple client authentication based on IP address. Only allow clients to connect if they are connecting from a trusted IP address. (see /etc/exports)
- An even bigger hammer: use SSH tunnels, IPsec, hardware assistance, and friends to get the security you need. Only downside is that this will dramatically decrease the performance of NFS.
- Users in NFS are modeled by UIDs
- What if on client A the user 'eggert' had UID == 1000 and on client B 'eggert' had UID == 1002
- If the file on the NFS server is owned by 1000, then 1002 can't access it even though they are both eggert
- Fix: Use some sort of authentication (kerberos, etc.)
Security
- Real world security is a huge topic. The main goals are to defend against force and fraud attacks. In the computer world fraud is the biggest concern.
- The main form attacks are against privacy, integrity (attacker can modify our information), and service (denail of service, DoS)
- In general the goals of computer security are to only allow authorized access (a positive goal) and to disallow unauthorized access (a negative goal -- harder to test)
Threat Modeling and Classification
The technique used to satisfy negative goals
- Usually the largest vulnerabilities are from insiders and social engineering attacks.
- The network can be the target of the attack
- Virus, drive by downloads
- DoS
- Buffer overruns
- Or devices can be attacked (ie: usb virii)
General mechanisms for (almost) any security scheme
- Authentication
- Integrity
- Authorization
- Auditing
- Plus your code must be correct and efficient
Authentication
- Prevents masquerading
- External (relies on 'sentry')
- these can be slow
- passwords/biometrics/secretkeys (managed by computers)
- must not be guessable/forgetable
- possible attacks include: shoulder surfing, key logging, phishing, and man in the middle attacks
- Based on (1) what you know, (2) what you have, or (3) who I am
- Internal
- Typically recorded in the process descriptor
- Maintained by the OS, so the user can't mess with them
- This must be fast
Cryptographic Building Blocks for Authentication
- Cryptographic hash function (h): h(m) -> V ; m is the message and V is the encrypted message. A good example is SHA1 which gives a 160bit hash value
- Symmetric Encryption (DES, 3DES)
- Must be very fast
- given P,K it must be easy to get {P}**K
- given {P}**K,K it must be easy to get P
- given {P}**K it must be hard to get P
- given P it must be hard to get {P}**K
- given P,{P}**K it must be hard to get K
- Unfortunately this relies on having a shared secret key