src/server/daemon/acl.c

Fri, 01 Mar 2013 22:44:54 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 01 Mar 2013 22:44:54 +0100
changeset 53
5ec9abba1027
parent 52
aced2245fb1c
child 54
3a1d5a52adfc
permissions
-rw-r--r--

fixed acl_evaluate

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


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 = access_mask | rq->aclreqaccess;
    
    
    // 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
                user->free(user);
                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) {
                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 {
                user->free(user);
            }
            return REQ_ABORTED;
        } 
        elm = elm->next;
    }
    
    // ok - all acls allowed access
    if(user) {
        user->free(user);
    }
    return REQ_PROCEED;
}

int wsacl_affects_user(ACLEntry *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(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];   
        if(wsacl_affects_user(ace, user)) {
            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