Tue, 30 Jan 2024 11:58:11 +0100
don't change the path bar value if the new path is a prefix of the current path
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2018 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 "pwd.h" #include <cx/buffer.h> #include <cx/utils.h> #include <cx/hash_map.h> #ifdef _WIN32 #include <winsock.h> #pragma comment(lib, "Ws2_32.lib") #else #include <netinet/in.h> #endif PwdStore* pwdstore_open(const char *file) { FILE *in = fopen(file, "r"); if(!in) { return NULL; } CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); fclose(in); if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) { cxBufferFree(buf); return NULL; } PwdStore *p = malloc(sizeof(PwdStore)); p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); p->content = buf; p->key = NULL; p->unlock_cmd = NULL; p->lock_cmd = NULL; p->encoffset = PWDS_HEADER_SIZE; p->isdecrypted = 0; if(pwdstore_getindex(p)) { pwdstore_free(p); return NULL; } return p; } PwdStore* pwdstore_new(void) { PwdStore *p = calloc(1, sizeof(PwdStore)); p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); PWDS_MAGIC(p) = PWDS_MAGIC_CHAR; PWDS_VERSION(p) = 1; PWDS_ENC(p) = DAV_KEY_AES256; PWDS_PWFUNC(p) = DAV_PWFUNC_PBKDF2_SHA256; dav_rand_bytes((unsigned char*)p->content->space+4, 16); p->isdecrypted = 1; p->encoffset = PWDS_HEADER_SIZE; return p; } static int readval(CxBuffer *in, char **val, int allowzero) { // value = length string // length = uint32 // string = bytes *val = NULL; // get length uint32_t length = 0; if(cxBufferRead(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) { return 0; } length = ntohl(length); // convert from BE to host byte order if(length == 0) { if(allowzero) { return 1; } else { return 0; } } if(length > PWDSTORE_MAX_LEN) { return 0; } // get value char *value = malloc(length + 1); value[length] = 0; if(cxBufferRead(value, 1, length, in) != length) { free(value); return 0; } *val = value; return 1; } static int read_indexentry(PwdStore *p, CxBuffer *in) { // read type of index element int type = cxBufferGet(in); if(type == EOF || type != 0) { // only type 0 supported yet return 0; } char *id = NULL; CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); locations->simple_destructor = free; // get id (required) int ret = 0; if(readval(in, &id, FALSE)) { ret = 1; // get locations char *location = NULL; while((ret = readval(in, &location, TRUE)) == 1) { if(!location) { break; } cxListAdd(locations, location); } } if(ret) { pwdstore_put_index(p, id, locations); } else { if(id) free(id); cxListDestroy(locations); } return ret; } static int read_pwdentry(PwdStore *p, CxBuffer *in) { int type = cxBufferGet(in); if(type == EOF || type != 0) { // only type 0 supported yet return 0; } char *id = NULL; char *location = NULL; char *user = NULL; char *password = NULL; int ret = 0; if(readval(in, &id, FALSE)) { if(readval(in, &user, FALSE)) { if(readval(in, &password, FALSE)) { pwdstore_put(p, id, user, password); ret = 1; } } } if(id) free(id); if(location) free(location); if(user) free(user); if(password) free(password); return ret; } static int remove_list_entries(PwdStore *s, const char *id) { int ret = 0; CxList *loc_entry = NULL; CxList *noloc_entry = NULL; CxMutIterator i = cxListMutIterator(s->locations); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); // TODO: break loop } } i = cxListMutIterator(s->noloc); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); // TODO: break loop } } return ret; } void pwdstore_remove_entry(PwdStore *s, const char *id) { while(remove_list_entries(s, id)) {} CxHashKey key = cx_hash_key_str(id); PwdIndexEntry *i = cxMapRemoveAndGet(s->index, key); PwdEntry *e = cxMapRemoveAndGet(s->ids, key); if(i) { cxListDestroy(i->locations); free(i->id); free(i); } if(e) { free(e->id); free(e->user); free(e->password); free(e); } } int pwdstore_getindex(PwdStore *s) { uint32_t netindexlen; // set the position to the last 4 bytes of the header // for reading index length s->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); // read indexlen and convert to host byte order if(cxBufferRead(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) { return 1; } uint32_t indexlen = ntohl(netindexlen); // integer overflow check if(UINT32_MAX - PWDS_HEADER_SIZE < indexlen) { return 1; } if(s->content->size < PWDS_HEADER_SIZE + indexlen) { return 1; } // encrypted content starts after the index content s->encoffset = PWDS_HEADER_SIZE + indexlen; // the index starts after the header CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0); index->size = indexlen; // read index while(read_indexentry(s, index)) {} // free index buffer structure (not the content) cxBufferFree(index); return 0; } int pwdstore_decrypt(PwdStore *p) { if(!p->key) { return 1; } if(p->isdecrypted) { return 0; } // decrypt contet size_t encsz = p->content->size - p->encoffset; CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0); enc->size = encsz; enc->size = p->content->size - p->encoffset; CxBuffer *content = aes_decrypt_buffer(enc, p->key); cxBufferFree(enc); if(!content) { return 1; } while(read_pwdentry(p, content)) {} cxBufferFree(content); return 0; } int pwdstore_setpassword(PwdStore *p, const char *password) { DavKey *key = dav_pw2key( password, (unsigned char*)(p->content->space + 4), 16, PWDS_PWFUNC(p), PWDS_ENC(p)); if(!key) { return 1; } p->key = key; return 0; } void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc) { PWDS_ENC(p) = enc; PWDS_PWFUNC(p) = pwfunc; } void pwdstore_free_entry(PwdEntry *e) { if(e->id) free(e->id); if(e->user) free(e->user); if(e->password) free(e->password); free(e); } void pwdstore_free(PwdStore* p) { p->ids->simple_destructor = (cx_destructor_func)pwdstore_free_entry; cxMapDestroy(p->ids); cxListDestroy(p->locations); if(p->content) { cxBufferFree(p->content); } free(p); } int pwdstore_has_id(PwdStore *s, const char *id) { return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0; } PwdEntry* pwdstore_get(PwdStore *p, const char *id) { PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id)); if(e && e->user && e->password) { return e; } else { return NULL; } } void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password) { PwdEntry *entry = malloc(sizeof(PwdEntry)); entry->id = strdup(id); entry->user = strdup(username); entry->password = strdup(password); cxMapPut(p->ids, cx_hash_key_str(id), entry); } void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) { PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id)); if(e) { return; } PwdIndexEntry *newentry = malloc(sizeof(PwdIndexEntry)); newentry->id = id; if(locations) { newentry->locations = locations; cxListAdd(p->locations, newentry); } else { newentry->locations = NULL; cxListAdd(p->noloc, newentry); } cxMapPut(p->index, cx_hash_key_str(id), newentry); } void write_index_entry(CxBuffer *out, PwdIndexEntry *e) { uint32_t idlen = strlen(e->id); uint32_t netidlen = htonl(idlen); cxBufferPut(out, 0); // type cxBufferWrite(&netidlen, 1, sizeof(uint32_t), out); cxBufferWrite(e->id, 1, idlen, out); CxIterator i = cxListIterator(e->locations); cx_foreach(char *, location, i) { uint32_t locationlen = strlen(location); uint32_t netlocationlen = htonl(locationlen); cxBufferWrite(&netlocationlen, 1, sizeof(uint32_t), out); cxBufferWrite(location, 1, locationlen, out); } uint32_t terminate = 0; cxBufferWrite(&terminate, 1, sizeof(uint32_t), out); } int pwdstore_store(PwdStore *p, const char *file) { if(!p->key) { return 1; } CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); // create index CxIterator i = cxListIterator(p->noloc); cx_foreach(PwdIndexEntry*, e, i) { write_index_entry(index, e); } i = cxListIterator(p->locations); cx_foreach(PwdIndexEntry*, e, i) { write_index_entry(index, e); } i = cxMapIteratorValues(p->ids); cx_foreach(PwdEntry*, value, i) { if(!value->id || !value->user || !value->password) { continue; } uint32_t idlen = strlen(value->id); uint32_t ulen = strlen(value->user); uint32_t plen = strlen(value->password); uint32_t netidlen = htonl(idlen); uint32_t netulen = htonl(ulen); uint32_t netplen = htonl(plen); // content buffer cxBufferPut(content, 0); // type cxBufferWrite(&netidlen, 1, sizeof(uint32_t), content); cxBufferWrite(value->id, 1, idlen, content); cxBufferWrite(&netulen, 1, sizeof(uint32_t), content); cxBufferWrite(value->user, 1, ulen, content); cxBufferWrite(&netplen, 1, sizeof(uint32_t), content); cxBufferWrite(value->password, 1, plen, content); } content->pos = 0; CxBuffer *enc = aes_encrypt_buffer(content, p->key); p->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); p->content->size = PWDS_HEADER_SIZE; // add index after header uint32_t netindexlen = htonl((uint32_t)index->size); cxBufferWrite(&netindexlen, 1, sizeof(uint32_t), p->content); cxBufferWrite(index->space, 1, index->size, p->content); // add encrypted buffer cxBufferWrite(enc->space, 1, enc->size, p->content); cxBufferFree(enc); FILE *out = fopen(file, "w"); if(!out) { return 1; } fwrite(p->content->space, 1, p->content->size, out); fclose(out); return 0; }