dav/config.c

Thu, 29 Jan 2015 11:43:41 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 29 Jan 2015 11:43:41 +0100
changeset 73
41e88442ad4e
parent 43
03076907b58a
child 74
da079dc0724c
permissions
-rw-r--r--

ssl version is now configurable

/*
 * 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 <string.h>
#include <sys/types.h>
#include <ucx/map.h>
#include <errno.h>
#include <libxml/tree.h>

#include "config.h"
#include <libidav/utils.h>

#define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
#define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b)

#ifdef _WIN32
#define ENV_HOME getenv("USERPROFILE")
#else
#define ENV_HOME getenv("HOME")
#endif /* _WIN32 */

static UcxMap *repos;
static UcxMap *keys;
static Proxy  *http_proxy;
static Proxy  *https_proxy;

int check_config_dir() {
    char *file = util_concat_path(ENV_HOME, ".dav");
    int ret = 0;
    if(util_mkdir(file, S_IRWXU)) {
        if(errno != EEXIST) {
            ret = 1;
        }
    }
    free(file);
    return ret;
}

static DavContext *context;

void load_config(DavContext *ctx) {
    context = ctx;
    // TODO: free the config somewhere
    repos = ucx_map_new(16);
    keys = ucx_map_new(16);
    http_proxy = calloc(1, sizeof(Proxy));
    https_proxy = calloc(1, sizeof(Proxy));
    if(check_config_dir()) {
        return;
    }
    
    char *file = util_concat_path(ENV_HOME, ".dav/config.xml");
    xmlDoc *doc = xmlReadFile(file, NULL, 0);
    if(!doc) {
        doc = xmlNewDoc(BAD_CAST "1.0");
        xmlNode *root = xmlNewNode(NULL, BAD_CAST "configuration");
        xmlDocSetRootElement(doc, root);
        xmlSaveFormatFileEnc(file, doc, "UTF-8", 1);
        xmlFreeDoc(doc);
        free(file);
        return;
    }
    free(file);
    
    xmlNode *xml_root = xmlDocGetRootElement(doc);
    xmlNode *node = xml_root->children;
    while(node) {
        if(node->type == XML_ELEMENT_NODE) {
            if(xstreq(node->name, "repository")) {
                load_repository(node);
            } else if(xstreq(node->name, "key")) {
                load_key(node);
            } else if (xstreq(node->name, "http-proxy")) {
                load_proxy(node, HTTP_PROXY);
            } else if (xstreq(node->name, "https-proxy")) {
                load_proxy(node, HTTPS_PROXY);
            }
        }
        node = node->next;
    }
    
    xmlFreeDoc(doc);
}

void load_repository(xmlNode *reponode) {
    xmlNode *node = reponode->children;
    Repository *repo = calloc(1, sizeof(Repository));
    repo->encrypt_name = false;
    repo->encrypt_content = false;
    repo->decrypt_name = false;
    repo->decrypt_content = true;
    repo->ssl_version = CURL_SSLVERSION_DEFAULT;
    while(node) {
        if(node->type == XML_ELEMENT_NODE) {
            char *value = util_xml_get_text(node);
            if(!value) {
                // next
            } else if(xstreq(node->name, "name")) {
                repo->name = strdup(value);
            } else if(xstreq(node->name, "url")) {
                repo->url = strdup(value);
            } else if(xstreq(node->name, "user")) {
                repo->user = strdup(value);
            } else if(xstreq(node->name, "password")) {
                repo->password = util_base64decode(value);
            } else if(xstreq(node->name, "default-key")) {
                repo->default_key = strdup(value);
            } else if(xstreq(node->name, "full-encryption")) {
                if(util_getboolean(value)) {
                    repo->encrypt_name = true;
                    repo->encrypt_content = true;
                    repo->decrypt_name = true;
                    repo->decrypt_content = true;
                }
            } else if(xstreq(node->name, "content-encryption")) {
                if(util_getboolean(value)) {
                    repo->encrypt_content = true;
                    repo->decrypt_content = true;
                } else {
                    repo->encrypt_content = false;
                }
            } else if(xstreq(node->name, "decrypt-content")) {
                repo->decrypt_content = util_getboolean(value);
            } else if(xstreq(node->name, "decrypt-name")) {
                repo->decrypt_name = util_getboolean(value);
            } else if(xstreq(node->name, "ssl-version")) {
                if(xstrEQ(value, "TLSv1")) {
                    repo->ssl_version = CURL_SSLVERSION_TLSv1;
                } else if(xstrEQ(value, "SSLv2")) {
                    repo->ssl_version = CURL_SSLVERSION_SSLv2;
                } else if(xstrEQ(value, "SSLv3")) {
                    repo->ssl_version = CURL_SSLVERSION_SSLv3;
                }
#if LIBCURL_VERSION_MAJOR >= 7
#if LIBCURL_VERSION_MINOR >= 34
                else if(xstrEQ(value, "TLSv1.0")) {
                    repo->ssl_version = CURL_SSLVERSION_TLSv1_0;
                } else if(xstrEQ(value, "TLSv1.1")) {
                    repo->ssl_version = CURL_SSLVERSION_TLSv1_1;
                } else if(xstrEQ(value, "TLSv1.2")) {
                    repo->ssl_version = CURL_SSLVERSION_TLSv1_2;
                }
#endif
#endif
                else {
                    fprintf(stderr, "Unknown ssl version: %s\n", value);
                }
            } else if(xstreq(node->name, "encrypt") || xstreq(node->name, "store-key-property") || xstreq(node->name, "decrypt")) {
                fprintf(stderr, "Error: config.xml contains deprecated elements\n");
                fprintf(stderr, "The elements <encrypt>, <decrypt> and <store-key-property> are removed\n");
                fprintf(stderr, "Use the following: \n\n");
                fprintf(stderr, "<content-encryption>true</content-encryption>\n");
                fprintf(stderr, "enables file content encryption and decryption\n\n");
                fprintf(stderr, "<full-encryption>true</full-encryption>\n");
                fprintf(stderr, "enables content and file name encryption/decryption\n\n");
                fprintf(stderr, "<decrypt-content>$BOOL</decrypt-content>\n");
                fprintf(stderr, "only enables/disables content decryption\n\n");
                fprintf(stderr, "<decrypt-name>$BOOL</decrypt-name>\n");
                fprintf(stderr, "only enables/disables name decryption\n\n");
                exit(-1);
            }
        }
        node = node->next;
    }
    
    if(!repo->name) {
        fprintf(
                stderr,
                "Cannot load config.xml: missing name for repository.\n");
        fprintf(stderr, "Abort.\n");
        exit(-1);
    }
    if(!repo->url) {
        fprintf(
                stderr,
                "Cannot load config.xml: "
                "missing url for repository '%s'.\n", repo->name);
        fprintf(stderr, "Abort.\n");
        exit(-1);
    }
    
    ucx_map_cstr_put(repos, repo->name, repo);
}

void load_proxy(xmlNode *proxynode, int type) {
    Proxy *proxy;
    const char *stype;
    if (type == HTTPS_PROXY) {
        proxy = https_proxy;
        stype = "https";
    } else if (type == HTTP_PROXY) {
        proxy = http_proxy;
        stype = "http";
    }
    
    xmlNode *node = proxynode->children;
    while(node) {
        if(node->type == XML_ELEMENT_NODE) {
            char *value = util_xml_get_text(node);
            if(!value) {
                // next
            } else if(xstreq(node->name, "url")) {
                proxy->url = strdup(value);
            } else if(xstreq(node->name, "user")) {
                proxy->user = strdup(value);
            } else if(xstreq(node->name, "password")) {
                proxy->password = util_base64decode(value);
            } else if(xstreq(node->name, "no")) {
                proxy->no = strdup(value);
            }
        }
        node = node->next;
    }
    
    if(!proxy->url) {
        fprintf(stderr,
            "Cannot load config.xml: missing url for %s proxy.\n", stype);
        fprintf(stderr, "Abort.\n");
        exit(-1);
    }
}

void load_key(xmlNode *keynode) {
    xmlNode *node = keynode->children;
    Key *key = calloc(1, sizeof(Key));
    key->type = KEY_AES256;
    
    while(node) {
        if(node->type == XML_ELEMENT_NODE) {
            char *value = util_xml_get_text(node);
            if(!value) {
                // next
            } else if(xstreq(node->name, "name")) {
                key->name = strdup(value);
            } else if(xstreq(node->name, "file")) {
                // load key file
                sstr_t key_data = load_key_file(value);
                if(key_data.length > 0) {
                    key->data = key_data.ptr;
                    key->length = key_data.length;
                }
            } else if(xstreq(node->name, "type")) {
                if(!strcmp(value, "aes128")) {
                    key->type = KEY_AES128;
                } else if(!strcmp(value, "aes256")) {
                    key->type = KEY_AES256;
                }
            }
                
        }
        node = node->next;
    }
    
    if(key->name) {
        if(key->type == KEY_AES128) {
            if(key->length < 16) {
                return;
            }
            key->length = 16;
        }
        if(key->type == KEY_AES256) {
            if(key->length < 32) {
                return;
            }
            key->length = 32;
        }
        ucx_map_cstr_put(keys, key->name, key);
        dav_context_add_key(context, key);
    } else {
        // TODO: free
    }
}

sstr_t load_key_file(char *filename) {
    sstr_t k;
    k.ptr = NULL;
    k.length = 0;
    
    FILE *file = NULL;
    if(filename[0] == '/') {
        file = fopen(filename, "r");
    } else {
        char *path = util_concat_path(ENV_HOME, ".dav/");
        char *p2 = util_concat_path(path, filename);
        file = fopen(p2, "r");
        free(path);
        free(p2);
    }
    
    if(!file) {
        return k;
    }
    
    char *data = malloc(256);
    size_t r = fread(data, 1, 256, file);
    k.ptr = data;
    k.length = r;
    
    fclose(file);
    return k;
} 

Repository* get_repository(sstr_t name) {
    if(!name.ptr) {
        return NULL;
    }
    return ucx_map_sstr_get(repos, name);
}

int get_repository_flags(Repository *repo) {
    int flags = 0;
    if(repo->decrypt_content) {
        flags |= DAV_SESSION_DECRYPT_CONTENT;
    }
    if(repo->decrypt_name) {
        flags |= DAV_SESSION_DECRYPT_NAME;
    }
    if(repo->encrypt_content) {
        flags |= DAV_SESSION_ENCRYPT_CONTENT;
    }
    if(repo->encrypt_name) {
        flags |= DAV_SESSION_ENCRYPT_NAME;
    }
    return flags;
}


Key* get_key(char *name) {
    if(!name) {
        return NULL;
    }
    return ucx_map_cstr_get(keys, name);
}

Proxy* get_http_proxy() {
    return http_proxy;
}

Proxy* get_https_proxy() {
    return https_proxy;
}

mercurial