UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2018 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 33 #include "pwd.h" 34 35 #include <cx/buffer.h> 36 #include <cx/utils.h> 37 #include <cx/hash_map.h> 38 39 #ifdef _WIN32 40 #include <winsock.h> 41 #pragma comment(lib, "Ws2_32.lib") 42 #else 43 #include <netinet/in.h> 44 #endif 45 46 PwdStore* pwdstore_open(const char *file) { 47 FILE *in = fopen(file, "r"); 48 if(!in) { 49 return NULL; 50 } 51 52 CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 53 cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); 54 fclose(in); 55 56 if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) { 57 cxBufferFree(buf); 58 return NULL; 59 } 60 61 PwdStore *p = malloc(sizeof(PwdStore)); 62 p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 63 p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 64 p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); 65 p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 66 p->content = buf; 67 p->key = NULL; 68 p->unlock_cmd = NULL; 69 p->lock_cmd = NULL; 70 p->encoffset = PWDS_HEADER_SIZE; 71 p->isdecrypted = 0; 72 73 if(pwdstore_getindex(p)) { 74 pwdstore_free(p); 75 return NULL; 76 } 77 78 return p; 79 } 80 81 PwdStore* pwdstore_new(void) { 82 PwdStore *p = calloc(1, sizeof(PwdStore)); 83 p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 84 p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 85 p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); 86 p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 87 p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 88 PWDS_MAGIC(p) = PWDS_MAGIC_CHAR; 89 PWDS_VERSION(p) = 1; 90 PWDS_ENC(p) = DAV_KEY_AES256; 91 PWDS_PWFUNC(p) = DAV_PWFUNC_PBKDF2_SHA256; 92 dav_rand_bytes((unsigned char*)p->content->space+4, 16); 93 p->isdecrypted = 1; 94 p->encoffset = PWDS_HEADER_SIZE; 95 return p; 96 } 97 98 static int readval(CxBuffer *in, char **val, int allowzero) { 99 // value = length string 100 // length = uint32 101 // string = bytes 102 103 *val = NULL; 104 105 // get length 106 uint32_t length = 0; 107 if(cxBufferRead(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) { 108 return 0; 109 } 110 length = ntohl(length); // convert from BE to host byte order 111 if(length == 0) { 112 if(allowzero) { 113 return 1; 114 } else { 115 return 0; 116 } 117 } 118 if(length > PWDSTORE_MAX_LEN) { 119 return 0; 120 } 121 122 // get value 123 char *value = malloc(length + 1); 124 value[length] = 0; 125 if(cxBufferRead(value, 1, length, in) != length) { 126 free(value); 127 return 0; 128 } 129 130 *val = value; 131 return 1; 132 } 133 134 static int read_indexentry(PwdStore *p, CxBuffer *in) { 135 // read type of index element 136 int type = cxBufferGet(in); 137 if(type == EOF || type != 0) { 138 // only type 0 supported yet 139 return 0; 140 } 141 142 char *id = NULL; 143 CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 144 locations->simple_destructor = free; 145 146 // get id (required) 147 int ret = 0; 148 if(readval(in, &id, FALSE)) { 149 ret = 1; 150 // get locations 151 char *location = NULL; 152 while((ret = readval(in, &location, TRUE)) == 1) { 153 if(!location) { 154 break; 155 } 156 cxListAdd(locations, location); 157 } 158 } 159 160 if(ret) { 161 pwdstore_put_index(p, id, locations); 162 } else { 163 if(id) free(id); 164 cxListDestroy(locations); 165 } 166 167 return ret; 168 } 169 170 static int read_pwdentry(PwdStore *p, CxBuffer *in) { 171 int type = cxBufferGet(in); 172 if(type == EOF || type != 0) { 173 // only type 0 supported yet 174 return 0; 175 } 176 177 char *id = NULL; 178 char *location = NULL; 179 char *user = NULL; 180 char *password = NULL; 181 182 int ret = 0; 183 if(readval(in, &id, FALSE)) { 184 if(readval(in, &user, FALSE)) { 185 if(readval(in, &password, FALSE)) { 186 pwdstore_put(p, id, user, password); 187 ret = 1; 188 } 189 } 190 } 191 192 if(id) free(id); 193 if(location) free(location); 194 if(user) free(user); 195 if(password) free(password); 196 197 return ret; 198 } 199 200 static int remove_list_entries(PwdStore *s, const char *id) { 201 int ret = 0; 202 203 CxList *loc_entry = NULL; 204 CxList *noloc_entry = NULL; 205 206 CxMutIterator i = cxListMutIterator(s->locations); 207 cx_foreach(PwdIndexEntry*, ie, i) { 208 if(!strcmp(ie->id, id)) { 209 cxIteratorFlagRemoval(i); 210 // TODO: break loop 211 } 212 } 213 i = cxListMutIterator(s->noloc); 214 cx_foreach(PwdIndexEntry*, ie, i) { 215 if(!strcmp(ie->id, id)) { 216 cxIteratorFlagRemoval(i); 217 // TODO: break loop 218 } 219 } 220 221 return ret; 222 } 223 224 void pwdstore_remove_entry(PwdStore *s, const char *id) { 225 while(remove_list_entries(s, id)) {} 226 227 CxHashKey key = cx_hash_key_str(id); 228 PwdIndexEntry *i = cxMapRemoveAndGet(s->index, key); 229 PwdEntry *e = cxMapRemoveAndGet(s->ids, key); 230 231 if(i) { 232 cxListDestroy(i->locations); 233 free(i->id); 234 free(i); 235 } 236 if(e) { 237 free(e->id); 238 free(e->user); 239 free(e->password); 240 free(e); 241 } 242 } 243 244 int pwdstore_getindex(PwdStore *s) { 245 uint32_t netindexlen; 246 247 // set the position to the last 4 bytes of the header 248 // for reading index length 249 s->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); 250 251 // read indexlen and convert to host byte order 252 if(cxBufferRead(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) { 253 return 1; 254 } 255 uint32_t indexlen = ntohl(netindexlen); 256 257 // integer overflow check 258 if(UINT32_MAX - PWDS_HEADER_SIZE < indexlen) { 259 return 1; 260 } 261 if(s->content->size < PWDS_HEADER_SIZE + indexlen) { 262 return 1; 263 } 264 // encrypted content starts after the index content 265 s->encoffset = PWDS_HEADER_SIZE + indexlen; 266 267 // the index starts after the header 268 CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0); 269 index->size = indexlen; 270 271 // read index 272 while(read_indexentry(s, index)) {} 273 274 // free index buffer structure (not the content) 275 cxBufferFree(index); 276 277 return 0; 278 } 279 280 int pwdstore_decrypt(PwdStore *p) { 281 if(!p->key) { 282 return 1; 283 } 284 if(p->isdecrypted) { 285 return 0; 286 } 287 288 // decrypt contet 289 size_t encsz = p->content->size - p->encoffset; 290 CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0); 291 enc->size = encsz; 292 enc->size = p->content->size - p->encoffset; 293 CxBuffer *content = aes_decrypt_buffer(enc, p->key); 294 cxBufferFree(enc); 295 if(!content) { 296 return 1; 297 } 298 299 while(read_pwdentry(p, content)) {} 300 301 cxBufferFree(content); 302 303 return 0; 304 } 305 306 int pwdstore_setpassword(PwdStore *p, const char *password) { 307 DavKey *key = dav_pw2key( 308 password, 309 (unsigned char*)(p->content->space + 4), 310 16, 311 PWDS_PWFUNC(p), 312 PWDS_ENC(p)); 313 if(!key) { 314 return 1; 315 } 316 317 p->key = key; 318 return 0; 319 } 320 321 void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc) { 322 PWDS_ENC(p) = enc; 323 PWDS_PWFUNC(p) = pwfunc; 324 } 325 326 void pwdstore_free_entry(PwdEntry *e) { 327 if(e->id) free(e->id); 328 if(e->user) free(e->user); 329 if(e->password) free(e->password); 330 free(e); 331 } 332 333 void pwdstore_free(PwdStore* p) { 334 p->ids->simple_destructor = (cx_destructor_func)pwdstore_free_entry; 335 cxMapDestroy(p->ids); 336 337 cxListDestroy(p->locations); 338 339 if(p->content) { 340 cxBufferFree(p->content); 341 } 342 343 free(p); 344 } 345 346 int pwdstore_has_id(PwdStore *s, const char *id) { 347 return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0; 348 } 349 350 PwdEntry* pwdstore_get(PwdStore *p, const char *id) { 351 PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id)); 352 if(e && e->user && e->password) { 353 return e; 354 } else { 355 return NULL; 356 } 357 } 358 359 void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password) { 360 PwdEntry *entry = malloc(sizeof(PwdEntry)); 361 entry->id = strdup(id); 362 entry->user = strdup(username); 363 entry->password = strdup(password); 364 cxMapPut(p->ids, cx_hash_key_str(id), entry); 365 } 366 367 void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) { 368 PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id)); 369 if(e) { 370 return; 371 } 372 PwdIndexEntry *newentry = malloc(sizeof(PwdIndexEntry)); 373 newentry->id = id; 374 if(locations) { 375 newentry->locations = locations; 376 cxListAdd(p->locations, newentry); 377 } else { 378 newentry->locations = NULL; 379 cxListAdd(p->noloc, newentry); 380 } 381 cxMapPut(p->index, cx_hash_key_str(id), newentry); 382 } 383 384 void write_index_entry(CxBuffer *out, PwdIndexEntry *e) { 385 uint32_t idlen = strlen(e->id); 386 uint32_t netidlen = htonl(idlen); 387 388 cxBufferPut(out, 0); // type 389 390 cxBufferWrite(&netidlen, 1, sizeof(uint32_t), out); 391 cxBufferWrite(e->id, 1, idlen, out); 392 393 CxIterator i = cxListIterator(e->locations); 394 cx_foreach(char *, location, i) { 395 uint32_t locationlen = strlen(location); 396 uint32_t netlocationlen = htonl(locationlen); 397 398 cxBufferWrite(&netlocationlen, 1, sizeof(uint32_t), out); 399 cxBufferWrite(location, 1, locationlen, out); 400 } 401 402 uint32_t terminate = 0; 403 cxBufferWrite(&terminate, 1, sizeof(uint32_t), out); 404 } 405 406 int pwdstore_store(PwdStore *p, const char *file) { 407 if(!p->key) { 408 return 1; 409 } 410 411 CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 412 CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 413 414 // create index 415 CxIterator i = cxListIterator(p->noloc); 416 cx_foreach(PwdIndexEntry*, e, i) { 417 write_index_entry(index, e); 418 } 419 i = cxListIterator(p->locations); 420 cx_foreach(PwdIndexEntry*, e, i) { 421 write_index_entry(index, e); 422 } 423 424 i = cxMapIteratorValues(p->ids); 425 cx_foreach(PwdEntry*, value, i) { 426 if(!value->id || !value->user || !value->password) { 427 continue; 428 } 429 430 uint32_t idlen = strlen(value->id); 431 uint32_t ulen = strlen(value->user); 432 uint32_t plen = strlen(value->password); 433 uint32_t netidlen = htonl(idlen); 434 uint32_t netulen = htonl(ulen); 435 uint32_t netplen = htonl(plen); 436 437 // content buffer 438 cxBufferPut(content, 0); // type 439 440 cxBufferWrite(&netidlen, 1, sizeof(uint32_t), content); 441 cxBufferWrite(value->id, 1, idlen, content); 442 cxBufferWrite(&netulen, 1, sizeof(uint32_t), content); 443 cxBufferWrite(value->user, 1, ulen, content); 444 cxBufferWrite(&netplen, 1, sizeof(uint32_t), content); 445 cxBufferWrite(value->password, 1, plen, content); 446 } 447 448 content->pos = 0; 449 CxBuffer *enc = aes_encrypt_buffer(content, p->key); 450 451 p->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); 452 p->content->size = PWDS_HEADER_SIZE; 453 454 // add index after header 455 uint32_t netindexlen = htonl((uint32_t)index->size); 456 cxBufferWrite(&netindexlen, 1, sizeof(uint32_t), p->content); 457 cxBufferWrite(index->space, 1, index->size, p->content); 458 459 // add encrypted buffer 460 cxBufferWrite(enc->space, 1, enc->size, p->content); 461 462 cxBufferFree(enc); 463 464 FILE *out = fopen(file, "w"); 465 if(!out) { 466 return 1; 467 } 468 fwrite(p->content->space, 1, p->content->size, out); 469 fclose(out); 470 471 return 0; 472 } 473