src/server/daemon/acl.c

Thu, 09 May 2013 19:41:11 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 09 May 2013 19:41:11 +0200
changeset 62
c47e081b6c0f
parent 54
3a1d5a52adfc
child 63
66442f81f823
permissions
-rw-r--r--

added keyfile based authentication

/*
 * 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 "../util/pool.h"
#include "../safs/auth.h"
#include "acl.h"

void acllist_createhandle(Session *sn, Request *rq) {
    ACLListHandle *handle = pool_malloc(sn->pool, sizeof(ACLListHandle));
    handle->defaultauthdb = NULL;
    handle->listhead = NULL;
    handle->listtail = NULL;
    rq->acllist = handle;
}

/*
 * append or prepend an ACL
 */
void acllist_add(Session *sn, Request *rq, ACLList *acl, int append) {
    if(!rq->acllist) {
        acllist_createhandle(sn, rq);
    }
    ACLListHandle *list = rq->acllist;
    
    if(!list->defaultauthdb && acl->authdb) {
        list->defaultauthdb = acl->authdb;
    }
    
    ACLListElm *elm = pool_malloc(sn->pool, sizeof(ACLListElm));
    elm->acl = acl;
    elm->next = NULL;
    if(list->listhead == NULL) {
        list->listhead = elm;
        list->listtail = elm;
    } else {
        if(append) {
            list->listtail->next = elm;
            list->listtail = elm;
        } else {
            elm->next = list->listhead;
            list->listhead = elm;
        }
    }
}

void acllist_append(Session *sn, Request *rq, ACLList *acl) {
    acllist_add(sn, rq, acl, 1);
}

void acllist_prepend(Session *sn, Request *rq, ACLList *acl) {
    acllist_add(sn, rq, acl, 0);
}

uint32_t acl_oflag2mask(int oflags) {
    /* TODO:
     * maybe there is a plattform where O_RDWR is not O_RDONLY | O_WRONLY
     */
    uint32_t access_mask = 0;
    if((oflags & O_RDONLY) == O_RDONLY) {
        access_mask |= ACL_READ_DATA;
    }
    if((oflags & O_WRONLY) == O_WRONLY) {
        access_mask |= ACL_WRITE_DATA;
    }
    return access_mask;
}

User* acllist_getuser(Session *sn, Request *rq, ACLListHandle *list) {
    if(!sn || !rq || !list) {
        return NULL;
    }
    
    // get user
    User *user = NULL;
    if(list->defaultauthdb) {
        char *usr;
        char *pw;
        if(!basicauth_getuser(sn, rq, &usr, &pw)) {
            user = list->defaultauthdb->get_user(list->defaultauthdb, usr);
            if(!user) {
                // wrong user name
                return NULL;
            }
            if(!user->verify_password(user, pw)) {
                // wrong password
                user->free(user);
                return NULL;
            }
            // ok - user is authenticated
        }
    }
    
    return user;
}

void acl_set_error_status(Session *sn, Request *rq, ACLList *acl, User *user) {
    if(!user) {
        char *value = NULL;
        if(acl->authprompt) {
            size_t realmlen = strlen(acl->authprompt);
            size_t len = realmlen + 16;
            value = pool_malloc(sn->pool, len);
            if(value) {
                snprintf(
                        value,
                        len,
                        "Basic realm=\"%s\"",
                        acl->authprompt);
            }
        }
        if(!value) {
            value = "Basic realm=\"login\"";
        }
        pblock_nvinsert("www-authenticate", value, rq->srvhdrs);
        protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
    } else {
        protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
    }
}

int acl_evaluate(Session *sn, Request *rq, int access_mask) {
    ACLListHandle *list = rq->acllist;
    if(!list) {
        return REQ_PROCEED;
    }
    
    // we combine access_mask with the required access rights
    access_mask |= rq->aclreqaccess;
    
    // get user
    User *user = acllist_getuser(sn, rq, list);
    
    // evalutate all ACLs
    ACLList *acl = acl_evallist(list, user, access_mask);
    if(acl) {
        acl_set_error_status(sn, rq, acl, user);
        // TODO: don't free the user here
        if(user) {
            user->free(user);
        }
        return REQ_ABORTED;
    }
    
    // access allowed, we can free the user
    if(user) {
        user->free(user);
    }
    
    return REQ_PROCEED;
}

ACLList* acl_evallist(ACLListHandle *list, User *user, int access_mask) {
    if(!list) {
        return NULL;
    }
    
    // evaluate each acl until one denies access
    ACLListElm *elm = list->listhead;
    while(elm) {
        ACLList *acl = elm->acl;
        if(!acl->check(acl, user, access_mask)) {
            // the acl denies access
            return acl;
        } 
        elm = elm->next;
    }
    
    // ok - all acls allowed access
    
    return NULL;
}

int wsacl_affects_user(WSAce *ace, User *user) {
    int check_access = 0;
    
    /*
     * an ace can affect
     *   a named user or group (ace->who is set)
     *   the owner of the resource (ACL_OWNER is set)
     *   the owning group of the resource (ACL_GROUP is set)
     *   everyone (ACL_EVERYONE is set)
     * 
     * Only one of this conditions should be true. The behavior on
     * illegal flag combination is undefined. We assume that the acls
     * are created correctly by the configuration loader.
     */
    
    if(ace->who && user) {
        // this ace is defined for a named user or group
        if((ace->flags & ACL_IDENTIFIER_GROUP) == ACL_IDENTIFIER_GROUP) {
            if(user->check_group(user, ace->who)) {
                // the user is in the group
                check_access = 1;
            }
        } else {
            if(!strcmp(user->name, ace->who)) {
                check_access = 1;
            }
        }
    } else if((ace->flags & ACL_OWNER) == ACL_OWNER) {
        // TODO
    } else if((ace->flags & ACL_GROUP) == ACL_GROUP) {
        // TODO
    } else if((ace->flags & ACL_EVERYONE) == ACL_EVERYONE) {
        check_access = 1;
    }
    
    return check_access;
}

int wsacl_check(WSAcl *acl, User *user, int access_mask) { 
    int allow = 0;
    uint32_t allowed_access = 0;
    // check each access control entry
    for(int i=0;i<acl->acenum;i++) {
        WSAce *ace = acl->ace[i];   
        if(wsacl_affects_user(ace, user)) {
            if(ace->type == ACL_TYPE_ALLOWED) {
                // add all new access rights 
                allowed_access |= (access_mask & ace->access_mask);
                // check if we have all requested rights
                if((allowed_access & access_mask) == access_mask) {
                    allow = 1;
                    break;
                }
            } else {
                // ACL_TYPE_DENIED
                
                if((ace->access_mask & access_mask) != 0) {
                    // access denied
                    break;
                }
            }
        }
    }
    
    // TODO: events
    
    return allow; // allow is 0, if no ace set it to 1
}

mercurial