UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 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 "pwdstore.h" 30 31 #include "utils.h" 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 37 #include <cx/utils.h> 38 #include <cx/hash_map.h> 39 40 #ifdef _WIN32 41 #include <winsock.h> 42 #pragma comment(lib, "Ws2_32.lib") 43 #else 44 #include <netinet/in.h> 45 #endif 46 47 48 static pwdstore_pwinput_func pw_input = (pwdstore_pwinput_func)pwdstore_default_pwinput; 49 static void *pw_input_data = "Master password: "; 50 51 char * pwdstore_default_pwinput(char *prompt) { 52 return util_password_input(prompt); 53 } 54 55 void pwdstore_set_pwinput_func(pwdstore_pwinput_func func, void *userdata) { 56 pw_input = func; 57 pw_input_data = userdata; 58 } 59 60 PwdStore* pwdstore_open(const char *file) { 61 FILE *in = fopen(file, "r"); 62 if(!in) { 63 return NULL; 64 } 65 66 CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 67 cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite); 68 fclose(in); 69 70 if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) { 71 cxBufferFree(buf); 72 return NULL; 73 } 74 75 PwdStore *p = malloc(sizeof(PwdStore)); 76 p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 77 p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 78 p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); 79 p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 80 p->content = buf; 81 p->key = NULL; 82 p->unlock_cmd = NULL; 83 p->lock_cmd = NULL; 84 p->encoffset = PWDS_HEADER_SIZE; 85 p->isdecrypted = 0; 86 87 if(pwdstore_getindex(p)) { 88 pwdstore_free(p); 89 return NULL; 90 } 91 92 return p; 93 } 94 95 PwdStore* pwdstore_new(void) { 96 PwdStore *p = calloc(1, sizeof(PwdStore)); 97 p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 98 p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 99 p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); 100 p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 101 p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 102 PWDS_MAGIC(p) = PWDS_MAGIC_CHAR; 103 PWDS_VERSION(p) = 1; 104 PWDS_ENC(p) = DAV_KEY_AES256; 105 PWDS_PWFUNC(p) = DAV_PWFUNC_PBKDF2_SHA256; 106 dav_rand_bytes((unsigned char*)p->content->space+4, 16); 107 p->isdecrypted = 1; 108 p->encoffset = PWDS_HEADER_SIZE; 109 return p; 110 } 111 112 PwdStore* pwdstore_clone(PwdStore *p) { 113 CxBuffer *newbuffer = calloc(1, sizeof(CxBuffer)); 114 *newbuffer = *p->content; 115 newbuffer->space = malloc(p->content->capacity); 116 memcpy(newbuffer->space, p->content->space, p->content->capacity); 117 118 DavKey *key = NULL; 119 if(p->key) { 120 key = malloc(sizeof(DavKey)); 121 key->data = malloc(p->key->length); 122 memcpy(key->data, p->key->data, p->key->length); 123 key->length = p->key->length; 124 key->type = p->key->type; 125 key->name = NULL; 126 } 127 128 PwdStore *newp = calloc(1, sizeof(PwdStore)); 129 newp->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 130 newp->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 131 newp->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS); 132 newp->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); 133 newp->content = newbuffer; 134 newp->key = key; 135 newp->unlock_cmd = p->unlock_cmd ? strdup(p->unlock_cmd) : NULL; 136 newp->lock_cmd = p->lock_cmd ? strdup(p->lock_cmd) : NULL; 137 newp->encoffset = p->encoffset; 138 newp->isdecrypted = p->isdecrypted; 139 140 CxIterator i = cxMapIterator(p->ids); 141 cx_foreach(CxMapEntry *, e, i) { 142 PwdEntry *entry = e->value; 143 pwdstore_put(newp, entry->id, entry->user, entry->password); 144 } 145 146 i = cxMapIterator(p->index); 147 cx_foreach(CxMapEntry *, e, i) { 148 PwdIndexEntry *entry = e->value; 149 CxList *locations = NULL; 150 if(entry->locations) { 151 locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 152 CxIterator li = cxListIterator(entry->locations); 153 cx_foreach(char *, location, li) { 154 cxListAdd(locations, strdup(location)); 155 } 156 } 157 pwdstore_put_index(newp, strdup(entry->id), locations); 158 } 159 160 return newp; 161 } 162 163 static int readval(CxBuffer *in, char **val, int allowzero) { 164 // value = length string 165 // length = uint32 166 // string = bytes 167 168 *val = NULL; 169 170 // get length 171 uint32_t length = 0; 172 if(cxBufferRead(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) { 173 return 0; 174 } 175 length = ntohl(length); // convert from BE to host byte order 176 if(length == 0) { 177 if(allowzero) { 178 return 1; 179 } else { 180 return 0; 181 } 182 } 183 if(length > PWDSTORE_MAX_LEN) { 184 return 0; 185 } 186 187 // get value 188 char *value = malloc(length + 1); 189 value[length] = 0; 190 if(cxBufferRead(value, 1, length, in) != length) { 191 free(value); 192 return 0; 193 } 194 195 *val = value; 196 return 1; 197 } 198 199 static int read_indexentry(PwdStore *p, CxBuffer *in) { 200 // read type of index element 201 int type = cxBufferGet(in); 202 if(type == EOF || type != 0) { 203 // only type 0 supported yet 204 return 0; 205 } 206 207 char *id = NULL; 208 CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS); 209 cxDefineDestructor(locations, free); 210 211 // get id (required) 212 int ret = 0; 213 if(readval(in, &id, FALSE)) { 214 // get locations 215 char *location = NULL; 216 while((ret = readval(in, &location, TRUE)) == 1) { 217 if(!location) { 218 break; 219 } 220 cxListAdd(locations, location); 221 } 222 } 223 224 if(ret) { 225 pwdstore_put_index(p, id, locations); 226 if(cxListSize(locations) == 0) { 227 cxListDestroy(locations); 228 } 229 } else { 230 if(id) free(id); 231 cxListDestroy(locations); 232 } 233 234 return ret; 235 } 236 237 static int read_pwdentry(PwdStore *p, CxBuffer *in) { 238 int type = cxBufferGet(in); 239 if(type == EOF || type != 0) { 240 // only type 0 supported yet 241 return 0; 242 } 243 244 char *id = NULL; 245 char *user = NULL; 246 char *password = NULL; 247 248 int ret = 0; 249 if(readval(in, &id, FALSE)) { 250 if(readval(in, &user, FALSE)) { 251 if(readval(in, &password, FALSE)) { 252 pwdstore_put(p, id, user, password); 253 ret = 1; 254 } 255 } 256 } 257 258 if(id) free(id); 259 if(user) free(user); 260 if(password) free(password); 261 262 return ret; 263 } 264 265 static void remove_list_entries(PwdStore *s, const char *id) { 266 CxIterator i = cxListMutIterator(s->locations); 267 cx_foreach(PwdIndexEntry*, ie, i) { 268 if(!strcmp(ie->id, id)) { 269 cxIteratorFlagRemoval(i); 270 cxIteratorNext(i); 271 break; 272 } 273 } 274 i = cxListMutIterator(s->noloc); 275 cx_foreach(PwdIndexEntry*, ie, i) { 276 if(!strcmp(ie->id, id)) { 277 cxIteratorFlagRemoval(i); 278 cxIteratorNext(i); 279 break; 280 } 281 } 282 } 283 284 void pwdstore_remove_entry(PwdStore *s, const char *id) { 285 remove_list_entries(s, id); 286 287 CxHashKey key = cx_hash_key_str(id); 288 PwdIndexEntry *i = cxMapRemoveAndGet(s->index, key); 289 PwdEntry *e = cxMapRemoveAndGet(s->ids, key); 290 291 if(i) { 292 if(i->locations) { 293 cxListDestroy(i->locations); 294 } 295 free(i->id); 296 free(i); 297 } 298 if(e) { 299 free(e->id); 300 free(e->user); 301 free(e->password); 302 free(e); 303 } 304 } 305 306 int pwdstore_getindex(PwdStore *s) { 307 uint32_t netindexlen; 308 309 // set the position to the last 4 bytes of the header 310 // for reading index length 311 s->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); 312 313 // read indexlen and convert to host byte order 314 if(cxBufferRead(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) { 315 return 1; 316 } 317 uint32_t indexlen = ntohl(netindexlen); 318 319 // integer overflow check 320 if(UINT32_MAX - PWDS_HEADER_SIZE < indexlen) { 321 return 1; 322 } 323 if(s->content->size < PWDS_HEADER_SIZE + indexlen) { 324 return 1; 325 } 326 // encrypted content starts after the index content 327 s->encoffset = PWDS_HEADER_SIZE + indexlen; 328 329 // the index starts after the header 330 CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0); 331 index->size = indexlen; 332 333 // read index 334 while(read_indexentry(s, index)) {} 335 336 // free index buffer structure (not the content) 337 cxBufferFree(index); 338 339 return 0; 340 } 341 342 int pwdstore_decrypt(PwdStore *p) { 343 if(!p->key) { 344 return 1; 345 } 346 if(p->isdecrypted) { 347 return 0; 348 } 349 350 // decrypt contet 351 size_t encsz = p->content->size - p->encoffset; 352 CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0); 353 enc->size = encsz; 354 enc->size = p->content->size - p->encoffset; 355 CxBuffer *content = aes_decrypt_buffer(enc, p->key); 356 cxBufferFree(enc); 357 if(!content) { 358 return 1; 359 } 360 361 while(read_pwdentry(p, content)) {} 362 363 cxBufferFree(content); 364 365 p->isdecrypted = 1; 366 367 return 0; 368 } 369 370 int pwdstore_setpassword(PwdStore *p, const char *password) { 371 DavKey *key = dav_pw2key( 372 password, 373 (unsigned char*)(p->content->space + 4), 374 16, 375 PWDS_PWFUNC(p), 376 PWDS_ENC(p)); 377 if(!key) { 378 return 1; 379 } 380 381 p->key = key; 382 return 0; 383 } 384 385 void pwdstore_encsettings(PwdStore *p, uint8_t enc, uint8_t pwfunc) { 386 PWDS_ENC(p) = enc; 387 PWDS_PWFUNC(p) = pwfunc; 388 } 389 390 void pwdstore_free_entry(PwdEntry *e) { 391 if(e->id) free(e->id); 392 if(e->user) free(e->user); 393 if(e->password) free(e->password); 394 free(e); 395 } 396 397 void pwdstore_free(PwdStore* p) { 398 cxDefineDestructor(p->ids, pwdstore_free_entry); 399 cxMapDestroy(p->ids); 400 401 cxListDestroy(p->locations); 402 403 if(p->content) { 404 cxBufferFree(p->content); 405 } 406 407 free(p); 408 } 409 410 int pwdstore_has_id(PwdStore *s, const char *id) { 411 return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0; 412 } 413 414 PwdEntry* pwdstore_get(PwdStore *p, const char *id) { 415 PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id)); 416 if(e && e->user && e->password) { 417 return e; 418 } else { 419 return NULL; 420 } 421 } 422 423 void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password) { 424 PwdEntry *entry = malloc(sizeof(PwdEntry)); 425 entry->id = strdup(id); 426 entry->user = strdup(username); 427 entry->password = strdup(password); 428 cxMapPut(p->ids, cx_hash_key_str(id), entry); 429 } 430 431 void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) { 432 PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id)); 433 if(e) { 434 return; 435 } 436 PwdIndexEntry *newentry = malloc(sizeof(PwdIndexEntry)); 437 newentry->id = id; 438 if(locations && cxListSize(locations) > 0) { 439 newentry->locations = locations; 440 cxListAdd(p->locations, newentry); 441 } else { 442 newentry->locations = NULL; 443 cxListAdd(p->noloc, newentry); 444 } 445 cxMapPut(p->index, cx_hash_key_str(id), newentry); 446 } 447 448 void write_index_entry(CxBuffer *out, PwdIndexEntry *e) { 449 uint32_t idlen = strlen(e->id); 450 uint32_t netidlen = htonl(idlen); 451 452 cxBufferPut(out, 0); // type 453 454 cxBufferWrite(&netidlen, 1, sizeof(uint32_t), out); 455 cxBufferWrite(e->id, 1, idlen, out); 456 457 if(e->locations) { 458 CxIterator i = cxListIterator(e->locations); 459 cx_foreach(char *, location, i) { 460 uint32_t locationlen = strlen(location); 461 uint32_t netlocationlen = htonl(locationlen); 462 463 cxBufferWrite(&netlocationlen, 1, sizeof(uint32_t), out); 464 cxBufferWrite(location, 1, locationlen, out); 465 } 466 } 467 468 uint32_t terminate = 0; 469 cxBufferWrite(&terminate, 1, sizeof(uint32_t), out); 470 } 471 472 int pwdstore_store(PwdStore *p, const char *file) { 473 if(!p->key) { 474 return 1; 475 } 476 477 CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 478 CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 479 480 // create index 481 CxIterator i = cxListIterator(p->noloc); 482 cx_foreach(PwdIndexEntry*, e, i) { 483 write_index_entry(index, e); 484 } 485 i = cxListIterator(p->locations); 486 cx_foreach(PwdIndexEntry*, e, i) { 487 write_index_entry(index, e); 488 } 489 490 i = cxMapIteratorValues(p->ids); 491 cx_foreach(PwdEntry*, value, i) { 492 if(!value->id || !value->user || !value->password) { 493 continue; 494 } 495 496 uint32_t idlen = strlen(value->id); 497 uint32_t ulen = strlen(value->user); 498 uint32_t plen = strlen(value->password); 499 uint32_t netidlen = htonl(idlen); 500 uint32_t netulen = htonl(ulen); 501 uint32_t netplen = htonl(plen); 502 503 // content buffer 504 cxBufferPut(content, 0); // type 505 506 cxBufferWrite(&netidlen, 1, sizeof(uint32_t), content); 507 cxBufferWrite(value->id, 1, idlen, content); 508 cxBufferWrite(&netulen, 1, sizeof(uint32_t), content); 509 cxBufferWrite(value->user, 1, ulen, content); 510 cxBufferWrite(&netplen, 1, sizeof(uint32_t), content); 511 cxBufferWrite(value->password, 1, plen, content); 512 } 513 514 content->pos = 0; 515 CxBuffer *enc = aes_encrypt_buffer(content, p->key); 516 517 p->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t); 518 p->content->size = PWDS_HEADER_SIZE; 519 520 // add index after header 521 uint32_t netindexlen = htonl((uint32_t)index->size); 522 cxBufferWrite(&netindexlen, 1, sizeof(uint32_t), p->content); 523 cxBufferWrite(index->space, 1, index->size, p->content); 524 525 // add encrypted buffer 526 cxBufferWrite(enc->space, 1, enc->size, p->content); 527 528 cxBufferFree(enc); 529 530 FILE *out = fopen(file, "w"); 531 if(!out) { 532 return 1; 533 } 534 fwrite(p->content->space, 1, p->content->size, out); 535 fclose(out); 536 537 return 0; 538 } 539 540 int pwdstore_decrypt_secrets(PwdStore *secrets) { 541 if(!pw_input) { 542 return 1; 543 } 544 545 char *ps_password = NULL; 546 if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) { 547 CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND); 548 if(!util_exec_command(secrets->unlock_cmd, cmd_out)) { 549 // command successful, get first line from output without newline 550 // and use that as password for the secretstore 551 size_t len = 0; 552 for(size_t i=0;i<=cmd_out->size;i++) { 553 if(i == cmd_out->size || cmd_out->space[i] == '\n') { 554 len = i; 555 break; 556 } 557 } 558 if(len > 0) { 559 ps_password = malloc(len + 1); 560 memcpy(ps_password, cmd_out->space, len); 561 ps_password[len] = 0; 562 } 563 } 564 cxBufferFree(cmd_out); 565 } 566 567 if(!ps_password) { 568 ps_password = pw_input(pw_input_data); 569 if(!ps_password) { 570 return 1; 571 } 572 } 573 574 int err = pwdstore_setpassword(secrets, ps_password); 575 free(ps_password); 576 if(err) { 577 fprintf(stderr, "Error: cannot create key from password\n"); 578 return 1; 579 } 580 if(pwdstore_decrypt(secrets)) { 581 fprintf(stderr, "Error: cannot decrypt secrets store\n"); 582 return 1; 583 } 584 return 0; 585 } 586