src/server/daemon/acl.c

Thu, 28 Feb 2013 20:00:05 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 28 Feb 2013 20:00:05 +0100
changeset 51
b28cf69f42e8
child 52
aced2245fb1c
permissions
-rw-r--r--

added acls

/*
 * 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;
}

void acllist_append(Session *sn, Request *rq, ACLList *acl) {
    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 {
        list->listtail->next = elm;
        list->listtail = elm;
    }
}

void acllist_prepend(Session *sn, Request *rq, ACLList *acl) {
    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 {
        elm->next = list->listhead;
        list->listhead = elm;
    }
}


int acl_evaluate(Session *sn, Request *rq, int access_mask) {
    ACLListHandle *list = rq->acllist;
    if(!list) {
        return REQ_PROCEED;
    }
    
    // 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 REQ_ABORTED;
            }
            if(!user->verify_password(user, pw)) {
                // wrong password
                return REQ_ABORTED;
            }
            // ok - user is authenticated
        }
    } else {
        // TODO
        return REQ_ABORTED;
    }
    
    // evaluate each acl until one denies access
    ACLListElm *elm = list->listhead;
    while(elm) {
        ACLList *acl = elm->acl;
        if(!wsacl_check(acl, user, access_mask)) {
            // the acl denies access
            
            if(!user) {
                pblock_nvinsert(
                        "www-authenticate",
                        "Basic realm=\"Webserver\"",
                        rq->srvhdrs);
                protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
            }
            return REQ_ABORTED;
        } 
        elm = elm->next;
    }
    
    // ok - all acls allowed access
    return REQ_PROCEED;
}

int wsacl_check(ACLList *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++) {
        ACLEntry *ace = acl->ace[i];
        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;
        }
        
        
        if(check_access) {
            if(ace->type == ACL_TYPE_ALLOWED) {
                // add all new access rights 
                allowed_access = 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;
}

mercurial