--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/vfs.c Sat Mar 16 23:11:34 2013 +0100 @@ -0,0 +1,265 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> + +#include "../util/pool.h" +#include "vfs.h" + +static VFS_IO sys_file_io = { + sys_file_read, + sys_file_write, + sys_file_close +}; + +VFSContext* vfs_request_context(Session *sn, Request *rq) { + VFSContext *ctx = pool_malloc(sn->pool, sizeof(VFSContext)); + ctx->sn = sn; + ctx->rq = rq; + ctx->vfs = rq->vfs; + ctx->user = acllist_getuser(sn, rq, rq->acllist); + ctx->acllist = rq->acllist; + ctx->aclreqaccess = rq->aclreqaccess; + ctx->pool = sn->pool; + ctx->vfs_errno = 0; + return ctx; +} + +SYS_FILE vfs_open(VFSContext *ctx, char *path, int oflags) { + Session *sn; + Request *rq; + pool_handle_t *pool; + uint32_t access_mask; + + if(ctx) { + access_mask = ctx->aclreqaccess; + access_mask |= acl_oflag2mask(oflags); + if(!ctx->pool) { + // TODO: log warning + // broken VFSContext + } + if(ctx->vfs) { + // ctx->aclreqaccess should be the complete access mask + uint32_t m = ctx->aclreqaccess; // save original access mask + ctx->aclreqaccess = access_mask; // set mask for vfs->open call + SYS_FILE file = ctx->vfs->open(ctx, path, oflags); + ctx->aclreqaccess = m; // restore original access mask + return file; + } else { + pool = ctx->pool; + } + } else { + sn = NULL; + rq = NULL; + pool = NULL; + access_mask = acl_oflag2mask(oflags); + } + + // check ACLs + uid_t uid; // uid and gid will be initialized by sys_acl_check + gid_t gid; + if(sys_acl_check(ctx, access_mask, &uid, &gid)) { + return NULL; + } + + // open file + int fd = open(path, oflags); + if(fd == -1) { + if(ctx) { + ctx->vfs_errno = errno; + sys_set_error_status(ctx); + } + return NULL; + } + + VFSFile *file = pool ? + pool_malloc(pool, sizeof(VFSFile)) : malloc(sizeof(VFSFile)); + if(!file) { + return NULL; + } + file->ctx = ctx; + file->data = NULL; + file->fd = fd; + file->io = &sys_file_io; + return file; +} + +SYS_FILE vfs_openRO(VFSContext *ctx, char *path) { + return vfs_open(ctx, path, O_RDONLY); +} + +SYS_FILE vfs_openWO(VFSContext *ctx, char *path) { + return vfs_open(ctx, path, O_WRONLY); +} + +SYS_FILE vfs_openRW(VFSContext *ctx, char *path) { + return vfs_open(ctx, path, O_RDONLY); +} + +int vfs_stat(VFSContext *ctx, char *path, struct stat *buf) { + Session *sn; + Request *rq; + uint32_t access_mask; + + if(ctx) { + access_mask = ctx->aclreqaccess; + access_mask |= ACL_READ_ATTRIBUTES; + if(!ctx->pool) { + // TODO: log warning + // broken VFSContext + } + if(ctx->vfs) { + // ctx->aclreqaccess should be the complete access mask + uint32_t m = ctx->aclreqaccess; // save original access mask + ctx->aclreqaccess = access_mask; // set mask for vfs->fstat call + int ret = ctx->vfs->stat(ctx, path, buf); + ctx->aclreqaccess = m; // restore original access mask + return ret; + } + } else { + sn = NULL; + rq = NULL; + access_mask = ACL_READ_ATTRIBUTES; + } + + // check ACLs + uid_t uid; // uid and gid will be initialized by sys_acl_check + gid_t gid; + if(sys_acl_check(ctx, access_mask, &uid, &gid)) { + return NULL; + } + + // stat + if(stat(path, buf)) { + if(ctx) { + ctx->vfs_errno = errno; + sys_set_error_status(ctx); + } + return -1; + } + + return 0; +} + +int vfs_fstat(VFSContext *ctx, SYS_FILE fd, struct stat *buf) { + if(ctx) { + if(!ctx->pool) { + // TODO: log warning + // broken VFSContext + } + if(ctx->vfs) { + return ctx->vfs->fstat(ctx, fd, buf); + } + } + + // stat + if(fstat(fd->fd, buf)) { + if(ctx) { + ctx->vfs_errno = errno; + } + return -1; + } + + return 0; +} + +void vfs_close(SYS_FILE fd) { + fd->io->close(fd); + if(fd->ctx) { + pool_free(fd->ctx->pool, fd); + } else { + free(fd); + } +} + + +// private + +int sys_acl_check(VFSContext *ctx, uint32_t acm, uid_t *uid, gid_t *gid) { + /* + * we don't allow remote root access, so a uid of 0 means that + * no file system acl check is needed + */ + *uid = 0; + *gid = 0; + if(!ctx) { + return 0; + } + + ACLListHandle *acllist = ctx->acllist; + if(acllist) { + ACLListElm *elm = acllist->listhead; + while(elm) { + ACLList *acl = elm->acl; + if(acl->isextern) { + // TODO + } else if(!acl->check(acl, ctx->user, acm)) { + // access denied + if(ctx->sn && ctx->rq) { + acl_set_error_status(ctx->sn, ctx->rq, acl, ctx->user); + } + return 1; + } + elm = elm->next; + } + } + + return 0; +} + +void sys_set_error_status(VFSContext *ctx) { + if(ctx->sn && ctx->rq) { + int status = 500; + switch(ctx->vfs_errno) { + case EACCES: { + status = 403; + break; + } + case ENOENT: { + status = 404; + break; + } + } + protocol_status(ctx->sn, ctx->rq, status, NULL); + } +} + +ssize_t sys_file_read(SYS_FILE fd, void *buf, size_t nbyte) { + return read(fd->fd, buf, nbyte); +} + +ssize_t sys_file_write(SYS_FILE fd, const void *buf, size_t nbyte) { + return write(fd->fd, buf, nbyte); +} + +void sys_file_close(SYS_FILE fd) { + close(fd->fd); +}