mod_privsep
Privilege Separation for Apache httpd

1. APR Virtual File System Proposal

A proposal for a VFS (Virtual File System) layer in APR (Apache Portable Runtime).

The proposed VFS will be similar to APVFS although with a number of key differences:

2. VFS Client API

The regular APR File IO layer would become a VFS dispatch layer and the current implementations functions would be renamed. e.g. apr_file_open becomes a VFS dispatch interface and the APR platform specific implementations would be renamed to apr_unix_file_open, apr_win32_file_open, apr_netware_file_open, apr_os2_file_open respectively.

This approach will add the VFS dispatch layer for all applications while maintaining 100% interface compatibility. The additional overhead will be negligible (a couple of pointer dereferences and a tail call) compared to the system call overhead.

Infrastructure common to multiple platforms (such as buffering) could move into apr_common_file_* functions that can be shared by the implementations. This would form the basis of inheritance in the VFS reducing the required number of operations a VFS provider module needs to implement.

2.1. VFS Context

2.2. Asynchronous IO

2.3. Types

2.3.1. apr_vfs_context_t

The apr_vfs_context_t type is a user handle that will be passed down to the VFS provider so that application implemented providers can have context specific behavior (request_rec* would be contained in the userdata pointer in httpd for example).

struct apr_vfs_context_t {
    void              *userdata;
};

2.3.2. apr_iocb_t

The apr_iocb contains a callback function for IO completion events and the results of completed IO.

struct apr_iocb_t {
    void              (*cb) (apr_vfs_iocb_t *iocb);
    apr_status_t      rv;
    apr_size_t        nbytes;
    void              *userdata;
};

2.4. New Functions

2.4.1. apr_vfs_context_new

Creates a apr_vfs_context_t to associate client userdata that is to be passed through to VFS providers when successive APR File IO calls are made (for code not converted to using the extended interface).

APR_DECLARE(apr_status_t) apr_vfs_context_new(apr_vfs_context_t **ctx, void *userdata, apr_pool_t *pool);

2.4.2. apr_vfs_thread_context_set

Sets a thread local context variable so that VFS providers can access the userdata in apr_vfs_context_t when the existing APR File routines are used (ones that do not have the additional apr_vfs_context_t and apr_iocb_t arguments).

APR_DECLARE(apr_status_t) apr_vfs_thread_context_set(apr_vfs_context_t *ctx);

2.4.3. apr_vfs_thread_context_get

Gets the thread local VFS context variable.

APR_DECLARE(apr_status_t) apr_vfs_thread_context_get(apr_vfs_context_t **ctx);

2.5. Extended Interface Functions

The existing APR File IO functions become the VFS dispatch layer (programs will not need to be changed to take advantage of the VFS). The existing APR File IO implementations will be renamed and be dispatched to by default.

The existing apr_stat interface will dispatch to the VFS provider with the calling thread's apr_vfs_context_t (set with apr_vfs_thread_context_set() and it may be NULL) and a NULL apr_iocb_t.

In addition _ex VFS dispatch interfaces will augment the standard interfaces to allow explicit passing of apr_vfs_context_t and apr_iocb_t for Asynchronous IO.

2.5.1. apr_stat_ex

Example of extended apr_stat_ex (with additional apr_vfs_context_t and apr_iocb_t arguments)

APR_DECLARE(apr_status_t) apr_stat_ex(apr_vfs_context_t *ctx,  /* may be NULL (overrides thread context) */
                                      apr_iocb_t        *iocb, /* NULL for synchronous calls */
                                      apr_finfo_t       *finfo, 
                                      const char        *fname, 
                                      apr_int32_t       wanted,
                                      apr_pool_t        *pool);

2.5.2. apr_file_open_ex

Example of extended apr_file_open_ex (with additional apr_vfs_context_t and apr_iocb_t arguments)

APR_DECLARE(apr_status_t) apr_file_open_ex(apr_vfs_context_t *ctx,  /* may be NULL (overrides thread context) */
                                           apr_iocb_t        *iocb, /* NULL for synchronous calls */
                                           apr_file_t        **new, 
                                           const char        *fname, 
                                           apr_int32_t       flag, 
                                           apr_fileperms_t   perm, 
                                           apr_pool_t        *pool);

2.5.3. ...

To do: document the rest of the extended File IO API functions ...

3. VFS Provider API

3.1. Types

It is proposed that apr_file_t and apr_dir_t will become VFS types and will no longer be opaque (and contain a common subset to support inherited buffering and the name attributes). The platform specific types will become apr_file_private_t and apr_dir_private_t and will contain the platform specific info stored in a *private member of the apr_file_t and apr_dir_t types. See section on analysis of common platform elements of the current apr_file_t and apr_dir_t below.

3.1.1. apr_vfs_mount_t

struct apr_vfs_mount_t {
    apr_vfs_mount_t   *next;
    apr_uint32_t      flags;
    char              *mntpoint;
    apr_vfs_opts_t    *vfsops;    /* VFS operations structure */
    apr_vfs_private_t *private;   /* Provider specific mount instance data */
};

3.1.2. apr_vfs_ops_t

struct apr_vfs_ops_t {
    apr_status_t (*stat)          (apr_vfs_mount_t *mnt, 
                                   apr_vfs_context_t *ctx, 
                                   apr_iocb_t *iocb, 
                                   apr_finfo_t *finfo, const char *fname, 
                                   apr_int32_t wanted, apr_pool_t *pool);
    apr_status_t (*file_open)     (apr_vfs_mount_t *mnt, 
                                   apr_vfs_context_t *ctx, 
                                   apr_iocb_t *iocb, 
                                   apr_file_t **new, const char *fname, 
                                   apr_int32_t flag, apr_fileperms_t perm, 
                                   apr_pool_t *pool);
    ...
};

3.1.3. apr_file_t

New apr_file_t that is visible to VFS providers (see section below on Inheritance and common file structure fields).

struct apr_file_t {
    apr_pool_t         *pool;
    apr_vfs_mount_t    *mnt;
    apr_file_private_t *private;
    apr_file_buffer_t  *buffer; /* non-null indicates buffering */
    char               *fname;
    apr_os_file_t      fd;      /* perhaps should be in private? */
    apr_int32_t        flags;
    int                eof_hit;
}

3.1.4. apr_file_buffer_t

And apr_file_buffer_t for inheritable buffering implementation.

struct apr_file_buffer_t {
    char               *buffer;
    apr_size_t         bufpos;
    apr_size_t         bufsize;
    apr_size_t         dataRead;
    int                direction;
    apr_off_t          filePtr;
    apr_thread_mutex_t *mutex;
}

3.1.5. apr_dir_t

New apr_dir_t that is visible to VFS providers.

struct apr_dir_t {
    apr_pool_t         *pool;
    apr_vfs_mount_t    *mnt;
    apr_dir_private_t  *private;
    char               *dirname;
}

3.2. New Functions

3.2.1. apr_vfs_mount

Add a virtual filesystem mount with a provider VFS implementation.

To do: define flags.

APR_DECLARE(apr_status_t) apr_vfs_mount(apr_vfs_mount_t **new,
                                        const char *mountpoint,
                                        apr_uint32_t flags,
                                        apr_vfs_ops_t *vfsops);

3.2.2. apr_vfs_unmount

Remove a virtual filesystem mount.

APR_DECLARE(apr_status_t) apr_vfs_unmount(apr_vfs_mount_t *mnt);

3.2.3. apr_vfs_mount_get_next

Iterate through current mounts. Passing in a NULL pointer will get the first mount. Passing in the current mount will get the next in the list until NULL is returned to indicate the end of the mount list.

APR_DECLARE(apr_status_t) apr_vfs_mount_get_next(apr_vfs_mount_t **mnt);

3.3. Provider Functions

This section shows sample interface changes to the existing APR File IO functions (which will become VFS provider functions and dispatched to by the new VFS layer).

3.3.1. apr_unix_stat

An example of a VFS provider implementation which in this case is the existing unix version of apr_stat that will be renamed and have 3 additional arguments.

APR_DECLARE(apr_status_t) apr_unix_stat(apr_vfs_mount_t *mnt, 
                                        apr_vfs_context_t *ctx, 
                                        apr_iocb_t *iocb,
                                        apr_finfo_t *finfo, 
                                        const char *fname, 
                                        apr_int32_t wanted,
                                        apr_pool_t *pool);

3.3.2. apr_unix_file_open

An example of a VFS provider implementation which in this case is the existing unix version of apr_file_open that will be renamed and have 3 additional arguments.

APR_DECLARE(apr_status_t) apr_unix_file_open(apr_vfs_mount_t *mnt, 
                                             apr_vfs_context_t *ctx, 
                                             apr_iocb_t *iocb, 
                                             apr_file_t **new, 
                                             const char *fname, 
                                             apr_int32_t flag, 
                                             apr_fileperms_t perm, 
                                             apr_pool_t *pool);

3.3.3. ...

To do: document the rest of the provider File IO API functions ...

3.4. Dispatching

Example apr_stat dispatch function (uses implicit apr_vfs_context_t from thread local)

APR_DECLARE(apr_status_t) apr_stat(apr_finfo_t *finfo, 
                                   const char *fname, 
                                   apr_int32_t wanted,
                                   apr_pool_t *pool)
{
    const apr_vfs_context_t *ctx = apr_vfs_get_thread_context();
    const apr_vfs_mount_t *mnt = apr_vfs_get_mount(ctx, fname);
    return mnt->vfsops->stat(mnt, ctx, NULL, finfo, fname, wanted, pool);
}

Example apr_stat_ex dispatch function (explicit apr_vfs_context_t)

APR_DECLARE(apr_status_t) apr_stat_ex(apr_vfs_context_t *ctx,  /* may be NULL */
                                      apr_iocb_t *iocb,        /* may be NULL for synchronous calls */
                                      apr_finfo_t *finfo, 
                                      const char *fname, 
                                      apr_int32_t wanted,
                                      apr_pool_t *pool)
{
    const apr_vfs_mount_t *mnt = apr_vfs_get_mount(ctx, fname);
    return mnt->vfsops->stat(mnt, ctx, iocb, finfo, fname, wanted, pool);
}

4. VFS Stacking

VFS modules may stack on top of underlying VFS modules and implement functions such as logging, performance timings, or implementing a stat cache.

A mount flag such as APR_VFS_MOUNT_STACK could be passed to apr_vfs_mount()

...

5. VFS Inheritance

VFS Provider Implementations will be able to inherit common VFS infrastructure such as buffered IO or apr_file_(read|write)_full implementations.

5.1. Common buffering

File IO functions that are candidates for a common implementation.

...

5.2. Analysis of existing apr_file_t

Analysis of common components of platform specific file types:

apr_file_t across APR platforms:

Unix

Win32

Netware

OS2

apr_pool_t *pool;

apr_pool_t *pool;

apr_pool_t *pool;

apr_pool_t *pool;

int filedes;

int filedes;

HANDLE filehand;

BOOLEAN pipe;

OVERLAPPED *pOverlapped;

HFILE filedes

int isopen;

char *fname;

char *fname;

char *fname;

char *fname;

apr_int32_t flags;

apr_int32_t flags;

apr_int32_t flags;

apr_int32_t flags;

int eof_hit;

int eof_hit;

int eof_hit;

int eof_hit;

int is_pipe;

int is_pipe;

int pipe;

int pipeSem;

apr_interval_time_t timeout;

apr_interval_time_t timeout;

apr_interval_time_t timeout;

int buffered;

BOOLEAN buffered;

int buffered;

int buffered;

enum { } blocking;

enum { } blocking;

enum { } blocking;

apr_finfo_t *finfo;

DWORD dwFileAttributes;

int append;

int ungetchar;

int ungetchar;

int ungetchar;

/* Wait IO */

apr_pollset_t *pollset;

apr_pollset_t *pollset;

apr_pollset_t *pollset;

/* Stuff for buffered mode */

char *buffer;

char *buffer

char *buffer

char *buffer;

apr_size_t bufpos;

apr_size_t bufpos;

apr_size_t bufpos;

apr_size_t bufpos;

apr_size_t bufsize;

apr_size_t bufsize;

apr_size_t bufsize;

apr_size_t bufsize;

unsigned long dataRead;

apr_size_t dataRead;

apr_size_t dataRead;

apr_size_t dataRead;

int direction;

int direction;

int direction;

int direction;

apr_off_t filePtr;

apr_off_t filePtr;

apr_off_t filePtr;

unsigned long filePtr;

/* threads /*

struct apr_thread_mutex_t *thlock;

apr_thread_mutex_t *mutex

struct apr_thread_mutex_t *thlock;

apr_thread_mutex_t *mutex

...