# HG changeset patch # User Olaf Wintermann # Date 1564312073 -7200 # Node ID dc3d70848c7c925115766beeff7e8190a6596d0d # Parent 3e4c0285a8688dc77d526c2a820a760bb58681df implement encrypted properties diff -r 3e4c0285a868 -r dc3d70848c7c dav/main.c --- a/dav/main.c Fri Jul 12 16:59:08 2019 +0200 +++ b/dav/main.c Sun Jul 28 13:07:53 2019 +0200 @@ -381,9 +381,8 @@ repo->url = strdup(url); *path = strdup("/"); } else if (strchr(url, '/')) { - repo->url = util_parent_path(url); - // TODO: check/fix - *path = strdup(util_resource_name(url)-1); + repo->url = util_url_base(url); + *path = util_url_path(url); } else { repo->url = strdup(url); *path = strdup("/"); diff -r 3e4c0285a868 -r dc3d70848c7c libidav/davqlexec.c --- a/libidav/davqlexec.c Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/davqlexec.c Sun Jul 28 13:07:53 2019 +0200 @@ -309,7 +309,7 @@ DavResourceData *data = (DavResourceData*)res->data; // add basic properties - char *value; + void *value; sstr_t cl_keystr = dav_property_key("DAV:", "getcontentlength"); UcxKey cl_key = ucx_key(cl_keystr.ptr, cl_keystr.length); diff -r 3e4c0285a868 -r dc3d70848c7c libidav/methods.c --- a/libidav/methods.c Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/methods.c Sun Jul 28 13:07:53 2019 +0200 @@ -135,6 +135,22 @@ return buf; } +UcxBuffer* create_cryptoprop_propfind_request(void) { + UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOFREE); + scstr_t s; + + s = SC("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = SC("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + s = SC("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + + return buf; +} + UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); sstr_t s; @@ -534,10 +550,35 @@ void add_properties(DavResource *res, ResponseTag *response) { res->iscollection = response->iscollection; + int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); + xmlNode *crypto_prop = NULL; + char *crypto_key = NULL; + // add properties UCX_FOREACH(elm, response->properties) { xmlNode *prop = elm->data; resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); + + if (decrypt_props && + prop->children && + prop->children->type == XML_TEXT_NODE && + xstreq(prop->ns->href, DAV_NS)) + { + if(xstreq(prop->name, "crypto-prop")) { + crypto_prop = prop; + } else if(xstreq(prop->name, "crypto-key")) { + crypto_key = util_xml_get_text(prop); + } + } + } + + if(crypto_prop && crypto_key) { + char *crypto_prop_content = util_xml_get_text(crypto_prop); + DavKey *key = dav_context_get_key(res->session->context, crypto_key); + if(crypto_prop_content) { + UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); + resource_set_crypto_properties(res, cprops); + } } set_davprops(res); @@ -667,12 +708,35 @@ res->iscollection = iscollection; // add properties + int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session); + xmlNode *crypto_prop = NULL; + UCX_FOREACH(elm, properties) { xmlNode *prop = elm->data; resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children); + + if (decrypt_props && + prop->children && + prop->children->type == XML_TEXT_NODE && + xstreq(prop->ns->href, DAV_NS)) + { + if(xstreq(prop->name, "crypto-prop")) { + crypto_prop = prop; + } + } } ucx_list_free(properties); + if(crypto_prop && crypto_key) { + char *crypto_prop_content = util_xml_get_text(crypto_prop); + DavKey *key = dav_context_get_key(res->session->context, crypto_key); + if(crypto_prop_content && key) { + UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content); + resource_set_crypto_properties(res, cprops); + } + } + + set_davprops(res); if(res != resource) { resource_add_child(resource, res); diff -r 3e4c0285a868 -r dc3d70848c7c libidav/methods.h --- a/libidav/methods.h Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/methods.h Sun Jul 28 13:07:53 2019 +0200 @@ -83,6 +83,7 @@ size_t length); UcxBuffer* create_allprop_propfind_request(void); +UcxBuffer* create_cryptoprop_propfind_request(void); UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt); UcxBuffer* create_basic_propfind_request(void); diff -r 3e4c0285a868 -r dc3d70848c7c libidav/resource.c --- a/libidav/resource.c Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/resource.c Sun Jul 28 13:07:53 2019 +0200 @@ -120,11 +120,13 @@ } void resource_free_properties(DavSession *sn, UcxMap *properties) { + if(!properties) return; + UcxMapIterator i = ucx_map_iterator(properties); - DavXmlNode *node; - UCX_MAP_FOREACH(key, node, i) { + DavProperty *property; + UCX_MAP_FOREACH(key, property, i) { // TODO: free everything - dav_session_free(sn, node); + dav_session_free(sn, property); } ucx_map_free(properties); } @@ -140,6 +142,7 @@ DavResourceData *data = res->data; resource_free_properties(sn, data->properties); + resource_free_properties(sn, data->crypto_properties); UCX_FOREACH(elm, data->set) { DavProperty *p = elm->data; @@ -221,10 +224,13 @@ return NULL; } data->properties = ucx_map_new_a(sn->mp->allocator, 32); + data->crypto_properties = NULL; data->set = NULL; data->remove = NULL; + data->crypto_set = NULL; + data->crypto_remove = NULL; + data->read = NULL; data->content = NULL; - data->read = NULL; data->seek = NULL; data->length = 0; return data; @@ -239,15 +245,29 @@ return resource->href; } +void resource_add_prop(DavResource *res, const char *ns, const char *name, DavXmlNode *val) { + DavSession *sn = res->session; + + DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace)); + namespace->prefix = NULL; + namespace->name = dav_session_strdup(sn, ns); + + DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty)); + prop->name = dav_session_strdup(sn, name); + prop->ns = namespace; + prop->value = val; + + sstr_t key = dav_property_key(ns, name); + ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, prop); + free(key.ptr); +} + void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val) { if(!val) { return; } - sstr_t key = dav_property_key(ns, name); - DavXmlNode *v = dav_convert_xml(res->session, val); - ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, v); - free(key.ptr); + resource_add_prop(res, ns, name, dav_convert_xml(res->session, val)); } void resource_add_string_property(DavResource *res, char *ns, char *name, char *val) { @@ -255,23 +275,45 @@ return; } - sstr_t key = dav_property_key(ns, name); - DavXmlNode *v = dav_text_node(res->session, val); - ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, v); - free(key.ptr); + resource_add_prop(res, ns, name, dav_text_node(res->session, val)); +} + +void resource_set_crypto_properties(DavResource *res, UcxMap *cprops) { + DavResourceData *data = res->data; + resource_free_properties(res->session, data->crypto_properties); + data->crypto_properties = cprops; } DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name) { sstr_t keystr = dav_property_key(ns, name); UcxKey key = ucx_key(keystr.ptr, keystr.length); - DavXmlNode *property = resource_get_property_k(res, key); + DavXmlNode *ret = resource_get_property_k(res, key); free(keystr.ptr); - return property; + + return ret; +} + +DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name) { + sstr_t keystr = dav_property_key(ns, name); + UcxKey key = ucx_key(keystr.ptr, keystr.length); + DavXmlNode *ret = resource_get_encrypted_property_k(res, key); + free(keystr.ptr); + + return ret; } DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key) { DavResourceData *data = (DavResourceData*)res->data; - return ucx_map_get(data->properties, key); + DavProperty *property = ucx_map_get(data->properties, key); + + return property ? property->value : NULL; +} + +DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key) { + DavResourceData *data = (DavResourceData*)res->data; + DavProperty *property = ucx_map_get(data->crypto_properties, key); + + return property ? property->value : NULL; } sstr_t dav_property_key(const char *ns, const char *name) { @@ -445,17 +487,37 @@ return dav_get_property_ns(res, pns, pname); } -DavXmlNode* dav_get_property_ns(DavResource *res, const char *ns, const char *name) { +static DavXmlNode* get_property_ns(DavResource *res, DavBool encrypted, const char *ns, const char *name) { if(!ns || !name) { return NULL; } - DavXmlNode *property = resource_get_property(res, ns, name); + DavResourceData *data = res->data; + + DavXmlNode *property = NULL; + UcxList *remove_list = NULL; + UcxList *set_list = NULL; + + if(encrypted) { + // check if crypto_properties because it will only be created + // if the resource has encrypted properties + if(!data->crypto_properties) { + return NULL; + } + property = resource_get_encrypted_property(res, ns, name); + remove_list = data->crypto_remove; + set_list = data->crypto_set; + } else { + property = resource_get_property(res, ns, name); + remove_list = data->remove; + set_list = data->set; + } + // resource_get_property only returns persistent properties // check the remove and set list if(property) { // if the property is in the remove list, we return NULL - UCX_FOREACH(elm, data->remove) { + UCX_FOREACH(elm, remove_list) { DavProperty *p = elm->data; if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return NULL; @@ -464,16 +526,31 @@ } // the set list contains property updates // we return an updated property if possible - UCX_FOREACH(elm, data->set) { + UCX_FOREACH(elm, set_list) { DavProperty *p = elm->data; if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) { return p->value; // TODO: fix } } // no property update + return property; } +DavXmlNode* dav_get_property_ns(DavResource *res, const char *ns, const char *name) { + DavXmlNode *property_value = get_property_ns(res, FALSE, ns, name); + + if(!property_value && DAV_DECRYPT_PROPERTIES(res->session)) { + property_value = get_property_ns(res, TRUE, ns, name); + } + + return property_value; +} + +DavXmlNode* dav_get_encrypted_property_ns(DavResource *res, const char *ns, const char *name) { + return get_property_ns(res, TRUE, ns, name); +} + static DavProperty* createprop(DavSession *sn, const char *ns, const char *name) { DavProperty *property = dav_session_malloc(sn, sizeof(DavProperty)); property->name = dav_session_strdup(sn, name); @@ -496,13 +573,18 @@ } void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value) { + DavSession *sn = res->session; UcxAllocator *a = res->session->mp->allocator; DavResourceData *data = res->data; DavProperty *property = createprop(res->session, ns, name); property->value = dav_text_node(res->session, value); - data->set = ucx_list_append_a(a, data->set, property); + if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { + data->crypto_set = ucx_list_append_a(a, data->crypto_set, property); + } else { + data->set = ucx_list_append_a(a, data->set, property); + } } void dav_set_property(DavResource *res, char *name, DavXmlNode *value) { @@ -513,13 +595,18 @@ } void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { - UcxAllocator *a = res->session->mp->allocator; + DavSession *sn = res->session; + UcxAllocator *a = sn->mp->allocator; DavResourceData *data = res->data; - DavProperty *property = createprop(res->session, ns, name); + DavProperty *property = createprop(sn, ns, name); property->value = value; // TODO: copy node? - data->set = ucx_list_append_a(a, data->set, property); + if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { + data->crypto_set = ucx_list_append_a(a, data->crypto_set, property); + } else { + data->set = ucx_list_append_a(a, data->set, property); + } } void dav_remove_property(DavResource *res, char *name) { @@ -530,12 +617,17 @@ } void dav_remove_property_ns(DavResource *res, char *ns, char *name) { + DavSession *sn = res->session; DavResourceData *data = res->data; UcxAllocator *a = res->session->mp->allocator; DavProperty *property = createprop(res->session, ns, name); - data->remove = ucx_list_append_a(a, data->remove, property); + if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) { + data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property); + } else { + data->remove = ucx_list_append_a(a, data->remove, property); + } } void dav_set_encrypted_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) { @@ -590,18 +682,14 @@ UcxMapIterator i = ucx_map_iterator(data->properties); - void *value; + DavProperty *value; int j = 0; UCX_MAP_FOREACH(key, value, i) { DavPropName *name = &names[j]; - // the map key is namespace + '\0' + name - name->ns = (char*)key.data; - for(int k=0;jname = (char*)key.data + k + 1; - break; - } - } + + name->ns = value->ns->name; + name->name = value->name; + j++; } @@ -833,6 +921,67 @@ } } + // generate crypto-prop content + if(DAV_ENCRYPT_PROPERTIES(sn) && sn->key && (data->crypto_set || data->crypto_remove)) { + DavResource *crypto_res = dav_resource_new_href(sn, res->href); + int ret = 1; + + if(crypto_res) { + UcxBuffer *rqbuf = create_cryptoprop_propfind_request(); + ret = dav_propfind(res->session, res, rqbuf); + ucx_buffer_free(rqbuf); + } + + if(!ret) { + DavXmlNode *crypto_prop_node = dav_get_property_ns(crypto_res, DAV_NS, "crypto-prop"); + UcxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node); + if(!crypto_props) { + // resource hasn't encrypted properties yet + crypto_props = ucx_map_new(32); // create new map + } + + // remove all properties + UCX_FOREACH(elm, data->crypto_remove) { + if(crypto_props->count == 0) { + break; // map already empty, can't remove any more + } + + DavProperty *property = elm->data; + sstr_t key = dav_property_key(property->ns->name, property->name); + DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key); + if(existing_prop) { + // TODO: free existing_prop + } + free(key.ptr); + } + + // set properties + UCX_FOREACH(elm, data->crypto_set) { + DavProperty *property = elm->data; + sstr_t key = dav_property_key(property->ns->name, property->name); + DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key); + ucx_map_sstr_put(crypto_props, key, property); + if(existing_prop) { + // TODO: free existing_prop + } + free(key.ptr); + } + + DavXmlNode *crypto_prop_value = create_crypto_prop(sn, crypto_props); + if(crypto_prop_value) { + DavProperty *new_crypto_prop = createprop(sn, DAV_NS, "crypto-prop"); + new_crypto_prop->value = crypto_prop_value; + data->set = ucx_list_prepend_a(sn->mp->allocator, data->set, new_crypto_prop); + } + + dav_resource_free(crypto_res); + } + + if(ret) { + return 1; + } + } + // store properties int r = 0; sn->error = DAV_OK; @@ -858,7 +1007,7 @@ ucx_buffer_free(request); ucx_buffer_free(response); } - + return r; } @@ -1352,13 +1501,17 @@ return ret; } -UcxMap* parse_crypto_prop(DavSession *sn, DavXmlNode *node) { +UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) { if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) { return NULL; } + return parse_crypto_prop_str(sn, key, node->content); +} + +UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) { size_t len = 0; - char *dec_str = aes_decrypt(node->content, &len, sn->key); + char *dec_str = aes_decrypt(content, &len, key); xmlDoc *doc = xmlReadMemory(dec_str, len, NULL, NULL, 0); free(dec_str); diff -r 3e4c0285a868 -r dc3d70848c7c libidav/resource.h --- a/libidav/resource.h Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/resource.h Sun Jul 28 13:07:53 2019 +0200 @@ -82,8 +82,11 @@ void resource_set_info(DavResource *res, char *href_str); DavResourceData* resource_data_new(DavSession *sn); void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val); +void resource_set_crypto_properties(DavResource *res, UcxMap *cprops); DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name); +DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name); DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key); +DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key); void resource_add_child(DavResource *parent, DavResource *child); void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr); int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash); @@ -91,7 +94,8 @@ sstr_t dav_property_key_a(UcxAllocator *a, const char *ns, const char *name); DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties); -UcxMap* parse_crypto_prop(DavSession *sn, DavXmlNode *node); +UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node); +UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content); #ifdef __cplusplus } diff -r 3e4c0285a868 -r dc3d70848c7c libidav/session.h --- a/libidav/session.h Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/session.h Sun Jul 28 13:07:53 2019 +0200 @@ -59,6 +59,12 @@ #define DAV_CRYPTO(sn) \ (DAV_ENCRYPT_NAME(sn) || DAV_DECRYPT_NAME(sn) || \ DAV_ENCRYPT_CONTENT(sn) || DAV_DECRYPT_CONTENT(sn)) + +#define DAV_ENCRYPT_PROPERTIES(sn) \ + (((sn)->flags & DAV_SESSION_ENCRYPT_PROPERTIES) == DAV_SESSION_ENCRYPT_PROPERTIES) + +#define DAV_DECRYPT_PROPERTIES(sn) \ + (((sn)->flags & DAV_SESSION_DECRYPT_PROPERTIES) == DAV_SESSION_DECRYPT_PROPERTIES) /* typedef struct DavPathCacheElement { diff -r 3e4c0285a868 -r dc3d70848c7c libidav/utils.c --- a/libidav/utils.c Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/utils.c Sun Jul 28 13:07:53 2019 +0200 @@ -321,6 +321,9 @@ } } } + if(!path) { + path = url + len; // empty string + } return path; } @@ -679,6 +682,11 @@ char* util_base64decode_len(const char* in, int *outlen) { /* code is mostly from wikibooks */ + if(!in) { + *outlen = 0; + return NULL; + } + size_t inlen = strlen(in); size_t bufsize = (inlen*3) / 4; char *outbuf = malloc(bufsize+1); diff -r 3e4c0285a868 -r dc3d70848c7c libidav/webdav.c --- a/libidav/webdav.c Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/webdav.c Sun Jul 28 13:07:53 2019 +0200 @@ -186,6 +186,26 @@ return ucx_map_sstr_get(context->namespaces, prefix); } +int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) { + DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns); + if(!info) { + info = calloc(1, sizeof(DavNSInfo)); + info->encrypt = encrypt; + ucx_map_cstr_put(context->namespaceinfo, ns, info); + } else { + info->encrypt = encrypt; + } + return 0; +} + +int dav_namespace_is_encrypted(DavContext *context, const char *ns) { + DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns); + if(info) { + return info->encrypt; + } + return 0; +} + void dav_get_property_namespace_str( DavContext *ctx, char *prefixed_name, @@ -301,7 +321,7 @@ int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf) { // clean resource properties DavResourceData *data = root->data; - ucx_map_clear(data->properties); + ucx_map_clear(data->properties); // TODO: free existing content CURL *handle = sn->handle; util_set_url(sn, dav_resource_get_href(root)); @@ -315,6 +335,7 @@ if(ret == CURLE_OK && status == 207) { //printf("response\n%s\n", rpbuf->space); dav_set_effective_href(sn, resource); + // TODO: use PropfindParser resource = parse_propfind_response(sn, resource, rpbuf); sn->error = DAV_OK; root->exists = 1; diff -r 3e4c0285a868 -r dc3d70848c7c libidav/webdav.h --- a/libidav/webdav.h Fri Jul 12 16:59:08 2019 +0200 +++ b/libidav/webdav.h Sun Jul 28 13:07:53 2019 +0200 @@ -58,6 +58,7 @@ typedef struct DavProperty DavProperty; typedef struct DavPropName DavPropName; typedef struct DavKey DavKey; +typedef struct DavNSInfo DavNSInfo; typedef struct DavXmlNode DavXmlNode; typedef struct DavXmlAttr DavXmlAttr; @@ -162,6 +163,7 @@ struct DavContext { UcxMap *namespaces; + UcxMap *namespaceinfo; UcxMap *keys; UcxList *sessions; DavProxy *http_proxy; @@ -201,6 +203,11 @@ size_t length; }; +struct DavNSInfo { + char *prefix; + DavBool encrypt; +}; + struct DavXmlNode { DavXmlNodeType type; @@ -233,6 +240,9 @@ int dav_add_namespace(DavContext *context, const char *prefix, const char *ns); DavNamespace* dav_get_namespace(DavContext *context, const char *prefix); +int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt); +int dav_namespace_is_encrypted(DavContext *context, const char *ns); + DavSession* dav_session_new(DavContext *context, char *base_url); DavSession* dav_session_new_auth( DavContext *context, @@ -301,6 +311,7 @@ DavXmlNode* dav_get_property(DavResource *res, char *name); DavXmlNode* dav_get_property_ns(DavResource *res, const char *ns, const char *name); +DavXmlNode* dav_get_encrypted_property_ns(DavResource *res, const char *ns, const char *name); char* dav_get_string_property(DavResource *res, char *name); char* dav_get_string_property_ns(DavResource *res, char *ns, char *name); void dav_set_string_property(DavResource *res, char *name, char *value);