# HG changeset patch # User Olaf Wintermann # Date 1443967060 -7200 # Node ID 37fb12574acd34ce8093044675bbf90b14c35c54 # Parent 509e9e1cbdccce2c69a58aa01847d20d71bbb31a added checksums for encrypted resources diff -r 509e9e1cbdcc -r 37fb12574acd dav/config.c --- a/dav/config.c Sat Oct 03 20:10:54 2015 +0200 +++ b/dav/config.c Sun Oct 04 15:57:40 2015 +0200 @@ -302,6 +302,7 @@ Key *key = calloc(1, sizeof(Key)); key->type = KEY_AES256; + int error = 0; while(node) { if(node->type == XML_ELEMENT_NODE) { char *value = util_xml_get_text(node); @@ -315,6 +316,9 @@ if(key_data.length > 0) { key->data = key_data.ptr; key->length = key_data.length; + } else { + fprintf(stderr, "Error: Cannot key from file: %s\n", value); + error = 1; } } else if(xstreq(node->name, "type")) { if(!strcmp(value, "aes128")) { @@ -330,15 +334,16 @@ node = node->next; } - if(key->name) { + if(!error && key->name) { + error = 0; if(key->type == KEY_AES128) { if(key->length < 16) { fprintf( stderr, - "Error: Key %s is too small (%d < 16)\n", + "Error: Key %s is too small (%zu < 16)\n", key->name, key->length); - return; + error = 1; } key->length = 16; } @@ -346,10 +351,10 @@ if(key->length < 32) { fprintf( stderr, - "Error: Key %s is too small (%d < 32)\n", + "Error: Key %s is too small (%zu < 32)\n", key->name, key->length); - return; + error = 1; } key->length = 32; } @@ -361,13 +366,20 @@ key->data = NULL; } } - ucx_map_cstr_put(keys, key->name, key); - dav_context_add_key(context, key); - } else { + + // add key to context + if(!error) { + ucx_map_cstr_put(keys, key->name, key); + dav_context_add_key(context, key); + } + } + + // cleanup + if(error) { if(key->data) { free(key->data); - free(key); } + free(key); } } diff -r 509e9e1cbdcc -r 37fb12574acd dav/main.c --- a/dav/main.c Sat Oct 03 20:10:54 2015 +0200 +++ b/dav/main.c Sun Oct 04 15:57:40 2015 +0200 @@ -187,12 +187,12 @@ if(repo->user) { user = repo->user; } else { - fprintf(stderr, "user: "); + fprintf(stderr, "User: "); fflush(stderr); user = fgets(ubuf, 256, stdin); } - char *password = util_password_input("password: "); + char *password = util_password_input("Password: "); size_t ulen = strlen(user); if(user[ulen-1] == '\n') { @@ -614,7 +614,7 @@ path, depth, t); - + if(!res) { print_resource_error(sn, path); return -1; diff -r 509e9e1cbdcc -r 37fb12574acd libidav/crypto.c --- a/libidav/crypto.c Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/crypto.c Sun Oct 04 15:57:40 2015 +0200 @@ -36,6 +36,7 @@ AESDecrypter* aes_decrypter_new(DavKey *key, void *stream, dav_write_func write_func) { AESDecrypter *dec = malloc(sizeof(AESDecrypter)); + SHA256_Init(&dec->sha256); dec->stream = stream; dec->write = write_func; dec->key = key; @@ -89,7 +90,8 @@ int outlen = len + 16; unsigned char *out = malloc(outlen); EVP_DecryptUpdate(&dec->ctx, out, &len, buf, len); - dec->write(out, 1, len, dec->stream); + ssize_t wlen = dec->write(out, 1, len, dec->stream); + SHA256_Update(&dec->sha256, out, wlen); free(out); return (s*n) / s; } @@ -113,13 +115,13 @@ } AESEncrypter *enc = malloc(sizeof(AESEncrypter)); + SHA256_Init(&enc->sha256); enc->stream = stream; enc->read = read_func; enc->tmp = NULL; enc->tmplen = 0; enc->tmpoff = 0; enc->end = 0; - //enc->iv = iv; enc->iv = iv; enc->ivlen = 16; @@ -158,6 +160,8 @@ void *in = malloc(len); size_t in_len = enc->read(in, 1, len, enc->stream); + SHA256_Update(&enc->sha256, in, in_len); + unsigned char *out = NULL; int outlen = 0; size_t ivl = enc->ivlen; @@ -173,12 +177,14 @@ out = malloc(16); EVP_EncryptFinal_ex(&enc->ctx, out, &outlen); enc->end = 1; + free(in); } enc->tmp = (char*)out; enc->tmplen = outlen + ivl; enc->tmpoff = 0; if(enc->iv) { + free(enc->iv); enc->iv = NULL; enc->ivlen = 0; } @@ -190,12 +196,15 @@ if(enc->tmp) { free(enc->tmp); } + if(enc->iv) { + free(enc->iv); + } EVP_CIPHER_CTX_cleanup(&enc->ctx); free(enc); } -char* aes_encrypt(char *in, DavKey *key) { +char* aes_encrypt(char *in, size_t len, DavKey *key) { unsigned char iv[16]; if(!RAND_bytes(iv, 16)) { return NULL; @@ -218,10 +227,11 @@ (unsigned char*)key->data, iv); } else { + EVP_CIPHER_CTX_cleanup(&ctx); return NULL; } - int len = strlen(in); + //int len = strlen(in); int buflen = len + 64; unsigned char *buf = calloc(1, buflen); memcpy(buf, iv, 16); @@ -233,10 +243,12 @@ EVP_EncryptFinal_ex(&ctx, buf + 16 + l, &f); char *out = util_base64encode((char*)buf, 16 + l + f); free(buf); + EVP_CIPHER_CTX_cleanup(&ctx); + return out; } -char* aes_decrypt(char *in, DavKey *key) { +char* aes_decrypt(char *in, size_t *length, DavKey *key) { int len; unsigned char *buf = (unsigned char*)util_base64decode_len(in, &len); @@ -257,6 +269,7 @@ key->data, buf); } else { + EVP_CIPHER_CTX_cleanup(&ctx); return NULL; } @@ -270,5 +283,17 @@ EVP_DecryptFinal_ex(&ctx, out + outlen, &f); out[outlen + f] = '\0'; free(buf); + EVP_CIPHER_CTX_cleanup(&ctx); + + *length = outlen + f; return (char*)out; } + + +void dav_get_hash(SHA256_CTX *sha256, char *buf) { +#ifdef __sun + SHA256Final(buf, sha256); +#else + SHA256_Final(buf, sha256); +#endif +} diff -r 509e9e1cbdcc -r 37fb12574acd libidav/crypto.h --- a/libidav/crypto.h Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/crypto.h Sun Oct 04 15:57:40 2015 +0200 @@ -33,12 +33,19 @@ #include #include +#ifdef __sun +#include +#else +#include +#endif + #ifdef __cplusplus extern "C" { #endif typedef struct { EVP_CIPHER_CTX ctx; + SHA256_CTX sha256; void *stream; dav_write_func write; DavKey *key; @@ -49,6 +56,7 @@ typedef struct { EVP_CIPHER_CTX ctx; + SHA256_CTX sha256; void *iv; size_t ivlen; void *stream; @@ -68,8 +76,10 @@ size_t aes_read(void *buf, size_t s, size_t n, AESEncrypter *enc); void aes_encrypter_close(AESEncrypter *enc); -char* aes_encrypt(char *in, DavKey *key); -char* aes_decrypt(char *in, DavKey *key); +char* aes_encrypt(char *in, size_t len, DavKey *key); +char* aes_decrypt(char *in, size_t *len, DavKey *key); + +void dav_get_hash(SHA256_CTX *sha256, char *buf); #ifdef __cplusplus } diff -r 509e9e1cbdcc -r 37fb12574acd libidav/davqlexec.c --- a/libidav/davqlexec.c Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/davqlexec.c Sun Oct 04 15:57:40 2015 +0200 @@ -240,18 +240,24 @@ ucx_map_put(new_properties, rt_key, value); } - UcxKey cn_key = dav_property_key("DAV:", "crypto-name"); + UcxKey cn_key = dav_property_key(DAV_NS, "crypto-name"); value = ucx_map_get(data->properties, cn_key); if(value) { ucx_map_put(new_properties, cn_key, value); } - UcxKey ck_key = dav_property_key("DAV:", "crypto-key"); + UcxKey ck_key = dav_property_key(DAV_NS, "crypto-key"); value = ucx_map_get(data->properties, ck_key); if(value) { ucx_map_put(new_properties, ck_key, value); } + UcxKey ch_key = dav_property_key(DAV_NS, "crypto-hash"); + value = ucx_map_get(data->properties, ch_key); + if(value) { + ucx_map_put(new_properties, ch_key, value); + } + // add properties from field list UCX_FOREACH(elm, fields) { DavCompiledField *field = elm->data; @@ -291,6 +297,7 @@ ucx_map_remove(data->properties, rt_key); ucx_map_remove(data->properties, cn_key); ucx_map_remove(data->properties, ck_key); + ucx_map_remove(data->properties, ch_key); resource_free_properties(sn, data->properties); data->properties = new_properties; @@ -302,6 +309,7 @@ free(rt_key.data); free(cn_key.data); free(ck_key.data); + free(ch_key.data); return 0; } @@ -378,7 +386,9 @@ ucx_mempool_destroy(mp); return result; } - ucx_mempool_reg_destr(mp, where, (ucx_destructor)ucx_buffer_free); + if(where) { + ucx_mempool_reg_destr(mp, where, (ucx_destructor)ucx_buffer_free); + } // compile order criterion UcxList *ordercr = NULL; @@ -495,15 +505,15 @@ add_properties(root, &response); cleanup_response(&response); - if(root == selroot) { + if(root == selroot) { // The current root is the root of the select query. // In this case we have to check the where clause. // If root is not selroot, the where clause was // already checked for the resource before it was // added to the stack. DavQLStackObj where_result; - if(!dav_exec_expr(where, root, &where_result)) { - if(where_result.data.integer != 0) { + if(!dav_exec_expr(where, root, &where_result)) { + if(where_result.data.integer != 0) { if(!reset_properties(sn, &result, root, cfieldlist)) { continue; } diff -r 509e9e1cbdcc -r 37fb12574acd libidav/methods.c --- a/libidav/methods.c Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/methods.c Sun Oct 04 15:57:40 2015 +0200 @@ -87,6 +87,7 @@ int add_crypto_name = 1; int add_crypto_key = 1; + int add_crypto_hash = 1; char *crypto_ns = "idav"; UcxMap *namespaces = ucx_map_new(8); UCX_FOREACH(elm, properties) { @@ -104,6 +105,9 @@ } else if(!strcmp(p->name, "crypto-key")) { add_crypto_key = 0; crypto_ns = p->ns->prefix; + } else if(!strcmp(p->name, "crypto-hash")) { + add_crypto_hash = 0; + crypto_ns = p->ns->prefix; } } } @@ -166,6 +170,12 @@ s = S(":crypto-key />\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); } + if(add_crypto_hash) { + ucx_buffer_putc(buf, '<'); + ucx_buffer_puts(buf, crypto_ns); + s = S(":crypto-hash />\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } } // extra properties @@ -214,6 +224,8 @@ ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); + s = S("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); s = S("\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); @@ -785,7 +797,7 @@ return buf; } -UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name) { +UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name, char *hash) { UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); sstr_t s; @@ -801,7 +813,7 @@ if(DAV_ENCRYPT_NAME(sn)) { s = S(""); ucx_buffer_write(s.ptr, 1, s.length, buf); - char *crname = aes_encrypt(name, key); + char *crname = aes_encrypt(name, strlen(name), key); ucx_buffer_puts(buf, crname); free(crname); s = S("\n"); @@ -814,6 +826,14 @@ s = S("\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); + if(hash) { + s = S(""); + ucx_buffer_write(s.ptr, 1, s.length, buf); + ucx_buffer_puts(buf, hash); + s = S("\n"); + ucx_buffer_write(s.ptr, 1, s.length, buf); + } + s = S("\n\n\n"); ucx_buffer_write(s.ptr, 1, s.length, buf); diff -r 509e9e1cbdcc -r 37fb12574acd libidav/methods.h --- a/libidav/methods.h Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/methods.h Sun Oct 04 15:57:40 2015 +0200 @@ -88,7 +88,7 @@ void set_davprops(DavResource *res); UcxBuffer* create_proppatch_request(DavResourceData *data); -UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name); +UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, char *name, char *hash); CURLcode do_delete_request(CURL *handle, UcxBuffer *response); diff -r 509e9e1cbdcc -r 37fb12574acd libidav/resource.c --- a/libidav/resource.c Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/resource.c Sun Oct 04 15:57:40 2015 +0200 @@ -84,7 +84,7 @@ // set name, path and href sstr_t n = sstr(name); res->name = sstrdup_a(sn->mp->allocator, n).ptr; - if(n.ptr[n.length-1] == '/') { + if(n.length > 0 && n.ptr[n.length-1] == '/') { res->name[n.length-1] = '\0'; } @@ -570,12 +570,18 @@ (dav_read_func)ucx_buffer_read); } - // create an empty resource + // put resource ret = do_put_request( sn->handle, enc, (dav_read_func)aes_read, 0); + + // get sha256 hash + char sha[SHA256_DIGEST_LENGTH]; + dav_get_hash(&enc->sha256, sha); + char *enc_hash = aes_encrypt(sha, SHA256_DIGEST_LENGTH, sn->key); + aes_encrypter_close(enc); if(buf) { ucx_buffer_free(buf); @@ -583,9 +589,12 @@ // add crypto properties // TODO: store the properties later - if(resource_add_crypto_info(sn, res->href, res->name)) { + if(resource_add_crypto_info(sn, res->href, res->name, enc_hash)) { + free(enc_hash); return 1; } + resource_add_property(res, DAV_NS, "crypto-hash", enc_hash); + free(enc_hash); } else { ret = do_put_request( sn->handle, @@ -663,13 +672,41 @@ CURLcode ret = curl_easy_perform(handle); + char *hash = NULL; if(dec) { + // get hash + char sha[SHA256_DIGEST_LENGTH]; + dav_get_hash(&dec->sha256, sha); + hash = util_hexstr(sha, 32); + aes_decrypter_close(dec); } int status = 0; curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && (status >= 200 && status < 300)) { + int verify_failed = 0; + if(DAV_DECRYPT_CONTENT(sn)) { + // try to verify the content + char *res_hash = dav_get_property_ns(res, DAV_NS, "crypto-hash"); + + if(res_hash) { + size_t len = 0; + char *dec_hash = aes_decrypt(res_hash, &len, sn->key); + char *hex_hash = util_hexstr(dec_hash, 32); + if(strcmp(hash, hex_hash)) { + verify_failed = 1; + } + free(dec_hash); + free(hex_hash); + } + } + + if(verify_failed) { + res->session->error = DAV_CONTENT_VERIFICATION_ERROR; + return 1; + } + res->session->error = DAV_OK; return 0; } else { @@ -733,7 +770,7 @@ if(name[len - 1] == '/') { name[len - 1] = '\0'; } - if(resource_add_crypto_info(sn, h, name)) { + if(resource_add_crypto_info(sn, h, name, NULL)) { // TODO: error } break; @@ -775,7 +812,7 @@ if(code == CURLE_OK && (s >= 200 && s < 300)) { sn->error = DAV_OK; // if the session has encrypted file names, add crypto infos - resource_add_crypto_info(sn, res->href, res->name); // TODO: check return type + resource_add_crypto_info(sn, res->href, res->name, NULL); // TODO: check return type // do a minimal propfind request UcxBuffer *rqbuf = create_propfind_request(sn, NULL); @@ -822,24 +859,27 @@ } -int resource_add_crypto_info(DavSession *sn, char *href, char *name) { +int resource_add_crypto_info(DavSession *sn, char *href, char *name, char *hash) { if(!DAV_IS_ENCRYPTED(sn)) { return 0; } - UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name); + UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash); UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); util_set_url(sn, href); CURLcode ret = do_proppatch_request(sn->handle, request, response); + ucx_buffer_free(request); int status = 0; curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status); if(ret == CURLE_OK && status == 207) { // TODO: parse response - sn->error = DAV_OK; + sn->error = DAV_OK; + ucx_buffer_free(response); return 0; } else { dav_session_set_error(sn, ret, status); + ucx_buffer_free(response); return 1; } } diff -r 509e9e1cbdcc -r 37fb12574acd libidav/resource.h --- a/libidav/resource.h Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/resource.h Sun Oct 04 15:57:40 2015 +0200 @@ -55,6 +55,11 @@ * content length */ size_t length; + + /* + * sha256 content hash + */ + char hash[32]; }; DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href); @@ -70,7 +75,7 @@ char* resource_get_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, char *href, char *name); +int resource_add_crypto_info(DavSession *sn, char *href, char *name, char *hash); UcxKey dav_property_key_a(UcxAllocator *a, char *ns, char *name); diff -r 509e9e1cbdcc -r 37fb12574acd libidav/session.c --- a/libidav/session.c Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/session.c Sun Oct 04 15:57:40 2015 +0200 @@ -264,6 +264,7 @@ // create resource for name lookup sstr_t rp = sstrdup(sstrn(path, start)); DavResource *root = dav_resource_new(sn, rp.ptr); + free(rp.ptr); resource_set_href(root, sstrn(href->space, href->pos)); // create request buffer for propfind requests @@ -326,6 +327,7 @@ // cleanup dav_resource_free_all(root); + ucx_buffer_free(rqbuf); ucx_buffer_free(pbuf); ucx_buffer_free(href); diff -r 509e9e1cbdcc -r 37fb12574acd libidav/utils.c --- a/libidav/utils.c Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/utils.c Sun Oct 04 15:57:40 2015 +0200 @@ -49,9 +49,6 @@ #include "crypto.h" #include "webdav.h" -#ifdef __sun -#include -#endif time_t util_parse_creationdate(char *str) { // example: 2012-11-29T21:35:35Z @@ -167,7 +164,7 @@ } int add_separator = 0; - if(base.ptr[base.length-1] == '/') { + if(base.length != 0 && base.ptr[base.length-1] == '/') { if(path.ptr[0] == '/') { base.length--; } @@ -268,7 +265,7 @@ size_t len = strlen(in); char *out = calloc(1, len); - BIO* b = BIO_new_mem_buf(in, len); + BIO *b = BIO_new_mem_buf(in, len); BIO *d = BIO_new(BIO_f_base64()); BIO_set_flags(d, BIO_FLAGS_BASE64_NO_NL); b = BIO_push(d, b); @@ -286,15 +283,16 @@ e = BIO_new(BIO_f_base64()); b = BIO_new(BIO_s_mem()); + BIO_set_flags(e, BIO_FLAGS_BASE64_NO_NL); e = BIO_push(e, b); BIO_write(e, in, len); BIO_flush(e); BIO_get_mem_ptr(e, &mem); - char *out = malloc(mem->length); - memcpy(out, mem->data, mem->length -1); - out[mem->length - 1] = '\0'; + char *out = malloc(mem->length + 1); + memcpy(out, mem->data, mem->length); + out[mem->length] = '\0'; BIO_free_all(e); @@ -312,7 +310,7 @@ } char* util_encrypt_str_k(DavSession *sn, char *str, DavKey *key) { - char *enc_str = aes_encrypt(str, key); + char *enc_str = aes_encrypt(str, strlen(str), key); char *ret_str = dav_session_strdup(sn, enc_str); free(enc_str); return ret_str; @@ -329,7 +327,8 @@ } char* util_decrypt_str_k(DavSession *sn, char *str, DavKey *key) { - char *dec_str = aes_decrypt(str, key); + size_t len = 0; + char *dec_str = aes_decrypt(str, &len, key); char *ret_str = dav_session_strdup(sn, dec_str); free(dec_str); return ret_str; @@ -478,3 +477,16 @@ free(prompt.ptr); return password; } + + +char* util_hexstr(unsigned char *data, size_t len) { + size_t buflen = 2*len + 4; + UcxBuffer *buf = ucx_buffer_new(malloc(buflen), buflen + 1, 0); + for(int i=0;ispace; + ucx_buffer_free(buf); + return str; +} diff -r 509e9e1cbdcc -r 37fb12574acd libidav/utils.h --- a/libidav/utils.h Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/utils.h Sun Oct 04 15:57:40 2015 +0200 @@ -91,6 +91,8 @@ void util_generate_key(DavKey *key, char *password); char* util_key_input(DavContext *ctx, DavKey *key); +char* util_hexstr(unsigned char *data, size_t len); + #ifdef __cplusplus } #endif diff -r 509e9e1cbdcc -r 37fb12574acd libidav/webdav.h --- a/libidav/webdav.h Sat Oct 03 20:10:54 2015 +0200 +++ b/libidav/webdav.h Sun Oct 04 15:57:40 2015 +0200 @@ -68,7 +68,8 @@ DAV_COULDNT_CONNECT, DAV_TIMEOUT, DAV_SSL_ERROR, - DAV_QL_ERROR + DAV_QL_ERROR, + DAV_CONTENT_VERIFICATION_ERROR }; typedef enum DavError DavError;