comparison pws-file.c @ 0:d541e748cfd8

Initial revision
author Guido Berhoerster <guido+libpws@berhoerster.name>
date Tue, 10 Feb 2015 11:29:54 +0100
parents
children ec5c1b653ee6
comparison
equal deleted inserted replaced
-1:000000000000 0:d541e748cfd8
1 /*
2 * Copyright (C) 2015 Guido Berhoerster <guido+libpws@berhoerster.name>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 #include "compat.h"
25
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <errno.h>
31 #ifdef HAVE_ENDIAN_H
32 #include <endian.h>
33 #endif /* HAVE_ENDIAN_H */
34 #ifdef HAVE_SYS_ENDIAN_H
35 #include <sys/endian.h>
36 #endif /* HAVE_ENDIAN_H */
37 #include <nettle/twofish.h>
38 #include <nettle/cbc.h>
39 #include <nettle/hmac.h>
40 #include <nettle/sha.h>
41
42 #include "pws-internal.h"
43
44 #define MAX_ITER (1 << 22)
45 #define DEFAULT_ITER 10000
46 #define KEY_SIZE 32UL
47 #define SALT_SIZE 32UL
48 #define METADATA_SIZE (sizeof (psafe3_tag) + SALT_SIZE + 4 +\
49 SHA256_DIGEST_SIZE + KEY_SIZE + KEY_SIZE + TWOFISH_BLOCK_SIZE)
50
51 static const unsigned char psafe3_tag[] = { 'P', 'W', 'S', '3' };
52 static const unsigned char eof_marker[] = { 'P', 'W', 'S', '3', '-', 'E', 'O',
53 'F', 'P', 'W', 'S', '3', '-', 'E', 'O', 'F' };
54
55 RB_HEAD(empty_groups_tree, pws3_field);
56
57 RB_HEAD(records_tree, pws3_record);
58
59 struct pws3_file {
60 struct pws3_field *fields[256];
61 struct empty_groups_tree *empty_groups_tree;
62 struct records_tree *records_tree;
63 struct pws_file_error {
64 enum pws_error_code code;
65 int errnum;
66 char *msg;
67 } error;
68 };
69
70 struct twofish_cbc_ctx CBC_CTX(struct twofish_ctx, TWOFISH_BLOCK_SIZE);
71
72 struct pws_file_ctx {
73 FILE *fp;
74 unsigned char *mem;
75 size_t mem_size;
76 size_t mem_pos;
77 struct twofish_cbc_ctx cipher_ctx;
78 struct hmac_sha256_ctx hmac_ctx;
79 struct pws3_file *pws_file;
80 uint32_t n_iter;
81 };
82
83 static int empty_groups_cmp(struct pws3_field *, struct pws3_field *);
84 static int record_cmp(struct pws3_record *, struct pws3_record *);
85 RB_PROTOTYPE_STATIC(empty_groups_tree, pws3_field, tree_entry, empty_groups_cmp)
86 RB_PROTOTYPE_STATIC(records_tree, pws3_record, tree_entry, record_cmp)
87
88 RB_GENERATE_STATIC(empty_groups_tree, pws3_field, tree_entry, empty_groups_cmp)
89
90 RB_GENERATE_STATIC(records_tree, pws3_record, tree_entry, record_cmp)
91
92 static int
93 empty_groups_cmp(struct pws3_field *field1, struct pws3_field *field2)
94 {
95 PWS_ASSERT(pws3_field_is_header(field1) &&
96 pws3_field_is_header(field2));
97 PWS_ASSERT((pws3_field_get_type(field1) ==
98 PWS3_HEADER_FIELD_EMPTY_GROUPS) &&
99 (pws3_field_get_type(field2) == PWS3_HEADER_FIELD_EMPTY_GROUPS));
100
101 return (strcmp(pws3_field_get_text(field1),
102 pws3_field_get_text(field2)));
103 }
104
105 static int
106 record_cmp(struct pws3_record *record1, struct pws3_record *record2)
107 {
108 struct pws3_field *uuid_field1;
109 struct pws3_field *uuid_field2;
110
111 uuid_field1 = pws3_record_get_field(record1, PWS3_RECORD_FIELD_UUID);
112 uuid_field2 = pws3_record_get_field(record2, PWS3_RECORD_FIELD_UUID);
113 PWS_ASSERT((uuid_field1 != NULL) && (uuid_field2 != NULL));
114
115 return (memcmp(pws3_field_get_uuid(uuid_field1),
116 pws3_field_get_uuid(uuid_field2), PWS3_UUID_SIZE));
117 }
118
119 static void
120 pws_set_system_error(struct pws3_file *pws_file, enum pws_error_code code,
121 int errnum, const char *fmt, ...)
122 {
123 char system_error_buf[4096] = "";
124 size_t system_error_len;
125 int error_len;
126 va_list args;
127 va_list args2;
128
129 pws_file->error.code = code;
130 pws_file->error.errnum = errnum;
131
132 strerror_r(errnum, system_error_buf, sizeof (system_error_buf) - 1);
133 system_error_len = strlen(system_error_buf);
134
135 pws_free(pws_file->error.msg, (pws_file->error.msg != NULL) ?
136 strlen(pws_file->error.msg) + 1 : 0);
137 if (fmt != NULL) {
138 va_start(args, fmt);
139 va_copy(args2, args);
140 error_len = vsnprintf(NULL, 0, fmt, args);
141 pws_file->error.msg = pws_alloc(error_len + 2 +
142 system_error_len + 1);
143 if (pws_file->error.msg == NULL) {
144 va_end(args2);
145 va_end(args);
146 return;
147 }
148 vsnprintf(pws_file->error.msg, error_len + 1, fmt, args2);
149 va_end(args2);
150 va_end(args);
151 strcpy(pws_file->error.msg + error_len, ": ");
152 strcpy(pws_file->error.msg + error_len + 2, system_error_buf);
153 } else {
154 pws_file->error.msg = pws_alloc(system_error_len + 1);
155 snprintf(pws_file->error.msg, system_error_len + 1, "%s",
156 system_error_buf);
157 }
158 }
159
160 static void
161 pws_set_error(struct pws3_file *pws_file, enum pws_error_code code,
162 const char *fmt, ...)
163 {
164 va_list args;
165 va_list args2;
166 int error_len;
167
168 pws_file->error.code = code;
169 pws_file->error.errnum = 0;
170
171 pws_free(pws_file->error.msg, (pws_file->error.msg != NULL) ?
172 strlen(pws_file->error.msg) + 1 : 0);
173 va_start(args, fmt);
174 va_copy(args2, args);
175 error_len = vsnprintf(NULL, 0, fmt, args);
176 pws_file->error.msg = pws_alloc(error_len + 1);
177 if (pws_file->error.msg == NULL) {
178 va_end(args2);
179 va_end(args);
180 return;
181 }
182 vsnprintf(pws_file->error.msg, error_len + 1, fmt, args2);
183 va_end(args2);
184 va_end(args);
185 }
186
187 static int
188 read_buf(struct pws_file_ctx *ctx, unsigned char *buf, size_t buf_size)
189 {
190 if (ctx->fp != NULL) {
191 if (fread(buf, 1, buf_size, ctx->fp) != buf_size) {
192 if (ferror(ctx->fp) != 0) {
193 return (-1);
194 } else if (feof(ctx->fp) != 0) {
195 return (1);
196 }
197 }
198 } else {
199 PWS_ASSERT(ctx->mem != NULL);
200 if (ctx->mem_size - ctx->mem_pos < buf_size) {
201 return (1);
202 }
203 memcpy(buf, &ctx->mem[ctx->mem_pos], buf_size);
204 ctx->mem_pos += buf_size;
205 }
206
207 return (0);
208 }
209
210 static int
211 write_buf(struct pws_file_ctx *ctx, const unsigned char *buf, size_t buf_size)
212 {
213 size_t remaining;
214 unsigned char *tmp;
215
216 if (ctx->fp != NULL) {
217 if (fwrite(buf, 1, buf_size, ctx->fp) != buf_size) {
218 return (-1);
219 }
220 if (fflush(ctx->fp) != 0) {
221 return (-1);
222 }
223 } else {
224 remaining = ctx->mem_size - ctx->mem_pos;
225 if (remaining < buf_size) {
226 tmp = pws_realloc(ctx->mem, ctx->mem_size +
227 (buf_size - remaining));
228 if (tmp == NULL) {
229 return (-1);
230 }
231 ctx->mem = tmp;
232 ctx->mem_size += (buf_size - remaining);
233 }
234 memcpy(&ctx->mem[ctx->mem_pos], buf, buf_size);
235 ctx->mem_pos += buf_size;
236 }
237
238 return (0);
239 }
240
241 static void
242 pws_file_clear(struct pws3_file *pws_file)
243 {
244 size_t i;
245 struct pws3_field *empty_group_field;
246 struct pws3_field *empty_group_field_tmp;
247 struct pws3_record *record;
248 struct pws3_record *record_tmp;
249
250 for (i = 0x00; i <= 0xff; i++) {
251 pws3_field_destroy(pws_file->fields[i]);
252 pws_file->fields[i] = NULL;
253 }
254
255 RB_FOREACH_SAFE(empty_group_field, empty_groups_tree,
256 pws_file->empty_groups_tree, empty_group_field_tmp) {
257 pws3_field_destroy(RB_REMOVE(empty_groups_tree,
258 pws_file->empty_groups_tree, empty_group_field));
259 }
260
261 RB_FOREACH_SAFE(record, records_tree, pws_file->records_tree,
262 record_tmp) {
263 pws3_record_destroy(RB_REMOVE(records_tree,
264 pws_file->records_tree, record));
265 }
266 }
267
268 void
269 pws3_file_destroy(struct pws3_file *pws_file)
270 {
271 if (pws_file == NULL) {
272 return;
273 }
274
275 pws_free(pws_file->error.msg, (pws_file->error.msg != NULL) ?
276 strlen(pws_file->error.msg) + 1 : 0);
277 pws_file_clear(pws_file);
278 pws_free(pws_file->empty_groups_tree,
279 sizeof (struct empty_groups_tree));
280 pws_free(pws_file->records_tree, sizeof (struct records_tree));
281 pws_free(pws_file, sizeof (struct pws3_file));
282 }
283
284 struct pws3_file *
285 pws3_file_create(void)
286 {
287 struct pws3_field *version_field = NULL;
288 struct pws3_file *pws_file = NULL;
289 size_t i;
290
291 /* version field is mandatory */
292 version_field = pws3_field_create(1, PWS3_HEADER_FIELD_VERSION);
293 if (version_field == NULL) {
294 goto err;
295 }
296 pws3_field_set_uint16(version_field, PWS3_VERSION);
297
298 pws_file = pws_alloc(sizeof (struct pws3_file));
299 if (pws_file == NULL) {
300 goto err;
301 }
302 for (i = 0x00; i <= 0xff; i++) {
303 pws_file->fields[i] = NULL;
304 }
305 pws_file->empty_groups_tree = NULL;
306 pws_file->records_tree = NULL;
307 pws_file->error.errnum = 0;
308 pws_file->error.code = 0;
309 pws_file->error.msg = NULL;
310
311 pws_file->empty_groups_tree =
312 pws_alloc(sizeof (struct empty_groups_tree));
313 if (pws_file->empty_groups_tree == NULL) {
314 goto err;
315 }
316 RB_INIT(pws_file->empty_groups_tree);
317
318 pws_file->records_tree = pws_alloc(sizeof (struct records_tree));
319 if (pws_file->records_tree == NULL) {
320 goto err;
321 }
322 RB_INIT(pws_file->records_tree);
323
324 pws3_file_set_header_field(pws_file, version_field);
325
326 return (pws_file);
327 err:
328 pws3_field_destroy(version_field);
329 if (pws_file != NULL) {
330 pws_free(pws_file->records_tree, sizeof (struct records_tree));
331 pws_free(pws_file->empty_groups_tree,
332 sizeof (struct empty_groups_tree));
333 }
334 pws_free(pws_file, sizeof (struct pws3_file));
335
336 return (NULL);
337 }
338
339 enum pws_error_code
340 pws3_file_get_error_code(struct pws3_file *pws_file)
341 {
342 return (pws_file->error.code);
343 }
344
345 const char *
346 pws3_file_get_error_message(struct pws3_file *pws_file)
347 {
348 return ((pws_file->error.msg != NULL) ? pws_file->error.msg : "");
349 }
350
351 static void
352 stretch_key(unsigned char *stretched_key, uint32_t n_iter, const char *key,
353 size_t key_size, const unsigned char *salt, size_t salt_size)
354 {
355 uint32_t i;
356 struct sha256_ctx md_ctx;
357
358 sha256_init(&md_ctx);
359 sha256_update(&md_ctx, key_size, (uint8_t *)key);
360 sha256_update(&md_ctx, salt_size, salt);
361 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
362
363 for (i = 0; i < n_iter; i++) {
364 sha256_update(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
365 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
366 }
367 }
368
369 static int
370 read_metadata(struct pws_file_ctx *ctx, const char *password)
371 {
372 int retval = -1;
373 unsigned char buf[METADATA_SIZE];
374 unsigned char *p = buf;
375 unsigned char *stretched_key = NULL;
376 unsigned char *key_k = NULL;
377 unsigned char *key_l = NULL;
378 int read_retval;
379 unsigned char salt[SALT_SIZE];
380 unsigned char key_digest[SHA256_DIGEST_SIZE];
381 struct sha256_ctx md_ctx;
382 struct twofish_ctx cipher_ctx;
383
384 stretched_key = pws_secure_alloc(SHA256_DIGEST_SIZE);
385 if (stretched_key == NULL) {
386 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
387 "out of memory");
388 goto out;
389 }
390
391 key_k = pws_secure_alloc(KEY_SIZE);
392 if (key_k == NULL) {
393 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
394 "out of memory");
395 goto out;
396 }
397
398 key_l = pws_secure_alloc(KEY_SIZE);
399 if (key_l == NULL) {
400 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
401 "out of memory");
402 goto out;
403 }
404
405 read_retval = read_buf(ctx, buf, sizeof (buf));
406 if (read_retval == 1) {
407 pws_set_error(ctx->pws_file, PWS_ERR_TRUNCATED_FILE,
408 "unexpected end of file");
409 goto out;
410 } else if (read_retval != 0) {
411 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
412 NULL);
413 goto out;
414 }
415
416 /* check tag */
417 if (memcmp(p, psafe3_tag, sizeof (psafe3_tag)) != 0) {
418 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
419 "unknown filetype");
420 goto out;
421 }
422 p += sizeof (psafe3_tag);
423
424 /* salt */
425 memcpy(salt, p, SALT_SIZE);
426 p += SALT_SIZE;
427
428 /* iterations */
429 memcpy(&ctx->n_iter, p, 4);
430 ctx->n_iter = le32toh(ctx->n_iter);
431 if ((ctx->n_iter < 1) || (ctx->n_iter > MAX_ITER)) {
432 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
433 "invalid number of iterations: %d", ctx->n_iter);
434 goto out;
435 }
436 p += 4;
437
438 /* verify password */
439 stretch_key(stretched_key, ctx->n_iter, password, strlen(password),
440 salt, SALT_SIZE);
441 sha256_init(&md_ctx);
442 sha256_update(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
443 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, key_digest);
444 if (memcmp(key_digest, p, SHA256_DIGEST_SIZE) != 0) {
445 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
446 "wrong password");
447 goto out;
448 }
449 p += SHA256_DIGEST_SIZE;
450
451 /* decrypt keys */
452 twofish_set_key(&cipher_ctx, KEY_SIZE, stretched_key);
453 twofish_decrypt(&cipher_ctx, KEY_SIZE, key_k, p);
454 p += KEY_SIZE;
455 twofish_decrypt(&cipher_ctx, KEY_SIZE, key_l, p);
456 p += KEY_SIZE;
457
458 /* set key for decryption */
459 twofish_set_key(&ctx->cipher_ctx.ctx, KEY_SIZE, key_k);
460
461 /* set IV */
462 CBC_SET_IV(&ctx->cipher_ctx, p);
463
464 /* set key for HMAC */
465 HMAC_SET_KEY(&ctx->hmac_ctx, &nettle_sha256, KEY_SIZE, key_l);
466
467 retval = 0;
468
469 out:
470 pws_secure_free(stretched_key, (stretched_key != NULL) ?
471 SHA256_DIGEST_SIZE : 0);
472 pws_secure_free(key_l, (key_l != NULL) ? KEY_SIZE : 0);
473 pws_secure_free(key_k, (key_k != NULL) ? KEY_SIZE : 0);
474
475 return (retval);
476 }
477
478 static int
479 read_block(struct pws_file_ctx *ctx, unsigned char *block)
480 {
481 unsigned char buf[TWOFISH_BLOCK_SIZE];
482 int read_retval;
483
484 read_retval = read_buf(ctx, buf, sizeof (buf));
485 if (read_retval == 1) {
486 pws_set_error(ctx->pws_file, PWS_ERR_TRUNCATED_FILE,
487 "unexpected end of file");
488 return (-1);
489 } else if (read_retval != 0) {
490 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
491 NULL);
492 return (-1);
493 }
494
495 /* reached the EOF block marking the end of encrypted records */
496 if (memcmp(buf, eof_marker, TWOFISH_BLOCK_SIZE) == 0) {
497 return (1);
498 }
499
500 CBC_DECRYPT(&ctx->cipher_ctx, twofish_decrypt, TWOFISH_BLOCK_SIZE,
501 block, buf);
502
503 return (0);
504 }
505
506 static int
507 read_field(struct pws_file_ctx *ctx, struct pws3_field **fieldp, int is_header)
508 {
509 int retval = -1;
510 enum pws_data_type data_type;
511 struct pws3_field *field = NULL;
512 unsigned char *block_buf = NULL;
513 unsigned char *p;
514 int read_retval;
515 unsigned char *field_buf = NULL;
516 uint32_t field_size;
517 size_t remaining;
518 size_t field_buf_size = 0;
519 uint8_t field_type = 0xff;
520 time_t data_time;
521 uint32_t data_uint32;
522 uint16_t data_uint16;
523 uint8_t data_uint8;
524
525 block_buf = pws_secure_alloc(TWOFISH_BLOCK_SIZE);
526 if (block_buf == NULL) {
527 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
528 "out of memory");
529 goto out;
530 }
531
532 next_field:
533 p = block_buf;
534
535 /* read first block */
536 read_retval = read_block(ctx, block_buf);
537 if (read_retval != 0) {
538 retval = read_retval;
539 goto out;
540 }
541
542 /* determine field length */
543 memcpy(&field_size, p, 4);
544 remaining = field_buf_size = field_size = le32toh(field_size);
545 p += 4;
546 /* determine field type */
547 memcpy(&field_type, p, 1);
548 p++;
549
550 /* check for end of header fields or end of record */
551 if ((is_header && (field_type == PWS3_HEADER_FIELD_END)) ||
552 (!is_header && (field_type == PWS3_RECORD_FIELD_END))) {
553 retval = 1;
554 goto out;
555 }
556
557 /* skip empty fields */
558 if (field_size == 0) {
559 goto next_field;
560 }
561
562 /* determine data type */
563 data_type = pws3_field_get_data_type(&(struct pws3_field){ .is_header =
564 is_header, .field_type = field_type });
565
566 /* make room for a terminating \0 in text fields */
567 if (data_type == PWS_DATA_TYPE_TEXT) {
568 field_buf_size++;
569 }
570
571 /* validate field length */
572 switch (data_type) {
573 case PWS_DATA_TYPE_UUID:
574 if ((field_size != 0) && (field_size != PWS3_UUID_SIZE)) {
575 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
576 "invalid field length");
577 goto out;
578 }
579 break;
580 case PWS_DATA_TYPE_TIME: /* FALLTHROUGH */
581 case PWS_DATA_TYPE_UINT32:
582 if ((field_size != 0) && (field_size != 4)) {
583 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
584 "invalid field length");
585 goto out;
586 }
587 break;
588 case PWS_DATA_TYPE_UINT8:
589 if ((field_size != 0) && (field_size != 1)) {
590 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
591 "invalid field length");
592 goto out;
593 }
594 break;
595 case PWS_DATA_TYPE_UINT16:
596 if ((field_size != 0) && (field_size != 2)) {
597 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
598 "invalid field length");
599 goto out;
600 }
601 break;
602 default:
603 /* text or bytes */
604 if ((!is_header &&
605 (field_type == PWS3_RECORD_FIELD_PASSWORD) &&
606 (field_buf_size > PWS3_MAX_PASSWORD_LEN)) ||
607 (field_buf_size > PWS3_MAX_FIELD_SIZE)) {
608 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
609 "invalid field length");
610 goto out;
611 }
612 }
613
614 field = pws3_field_create(is_header, field_type);
615 if (field == NULL) {
616 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY, errno,
617 NULL);
618 goto out;
619 }
620
621 /* create field */
622 if (field_buf_size > 0) {
623 if (!is_header && (field_type == PWS3_RECORD_FIELD_PASSWORD)) {
624 field_buf = pws_secure_alloc(field_buf_size);
625 } else {
626 field_buf = pws_alloc(field_buf_size);
627 }
628 if (field_buf == NULL) {
629 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
630 "out of memory");
631 goto out;
632 }
633 memset(field_buf, 0, field_buf_size);
634
635 memcpy(field_buf, p, MIN(remaining,
636 (size_t)TWOFISH_BLOCK_SIZE - (p - block_buf)));
637 remaining -= MIN(remaining,
638 (size_t)TWOFISH_BLOCK_SIZE - (p - block_buf));
639
640 while (remaining > 0) {
641 read_retval = read_block(ctx, block_buf);
642 if (read_retval != 0) {
643 goto out;
644 }
645 memcpy(field_buf + (field_size - remaining),
646 block_buf, MIN(remaining, TWOFISH_BLOCK_SIZE));
647 remaining -= MIN(remaining, TWOFISH_BLOCK_SIZE);
648 }
649
650 hmac_sha256_update(&ctx->hmac_ctx, field_size, field_buf);
651
652 switch (data_type) {
653 case PWS_DATA_TYPE_UUID:
654 retval = pws3_field_set_uuid(field, field_buf);
655 break;
656 case PWS_DATA_TYPE_TEXT:
657 retval = pws3_field_set_text(field, (char *)field_buf);
658 break;
659 case PWS_DATA_TYPE_TIME:
660 memcpy(&data_uint32, field_buf, 4);
661 data_time = le32toh(data_uint32);
662 retval = pws3_field_set_time(field, data_time);
663 break;
664 case PWS_DATA_TYPE_UINT8:
665 memcpy(&data_uint8, field_buf, 1);
666 retval = pws3_field_set_uint8(field, data_uint8);
667 break;
668 case PWS_DATA_TYPE_UINT16:
669 memcpy(&data_uint16, field_buf, 2);
670 data_uint16 = le16toh(data_uint16);
671 retval = pws3_field_set_uint16(field, data_uint16);
672 break;
673 case PWS_DATA_TYPE_UINT32:
674 memcpy(&data_uint32, field_buf, 4);
675 data_uint32 = le32toh(data_uint32);
676 retval = pws3_field_set_uint32(field, data_uint32);
677 break;
678 case PWS_DATA_TYPE_BYTES:
679 retval = pws3_field_set_bytes(field, field_buf,
680 field_buf_size);
681 }
682 if (retval != 0) {
683 goto out;
684 }
685 }
686
687 retval = 0;
688
689 out:
690 if (!is_header && (field_type == PWS3_RECORD_FIELD_PASSWORD)) {
691 pws_secure_free(field_buf, field_buf_size);
692 } else {
693 pws_free(field_buf, field_buf_size);
694 }
695 pws_secure_free(block_buf, (block_buf != NULL) ?
696 (size_t)TWOFISH_BLOCK_SIZE : 0);
697
698 if (retval == 0) {
699 *fieldp = field;
700 } else {
701 pws3_field_destroy(field);
702 }
703
704 return (retval);
705 }
706
707 static int
708 read_header(struct pws_file_ctx *ctx)
709 {
710 int retval;
711 struct pws3_field *field = NULL;
712
713 /* the header must start with a version field */
714 retval = read_field(ctx, &field, 1);
715 if (retval != 0) {
716 /* error or end of headers */
717 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
718 "header does not start with a version field");
719 return (-1);
720 } else if (field->field_type != PWS3_HEADER_FIELD_VERSION) {
721 /* header does not start with a version field */
722 pws3_field_destroy(field);
723 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
724 "header does not start with a version field");
725 return (-1);
726 } else if (field->value.uint16 > PWS3_VERSION) {
727 /* unsupported database version */
728 pws3_field_destroy(field);
729 pws_set_error(ctx->pws_file, PWS_ERR_UNSUPPORTED_VERSION,
730 "unsupported database version");
731 return (-1);
732 }
733 pws3_file_set_header_field(ctx->pws_file, field);
734
735 for (;;) {
736 retval = read_field(ctx, &field, 1);
737 if (retval == 1) {
738 /* end of headers */
739 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_HEADER,
740 "unexpected end of headers");
741 break;
742 } else if (retval != 0) {
743 return (-1);
744 }
745 pws3_file_set_header_field(ctx->pws_file, field);
746 }
747
748 return (0);
749 }
750
751 static int
752 read_records(struct pws_file_ctx *ctx)
753 {
754 int retval;
755 struct pws3_record *record = NULL;
756 struct pws3_field *field = NULL;
757
758 for (;;) {
759 /*
760 * a record must consist of at least three fields, instead of
761 * the first field there could also be an EOF marker
762 */
763 retval = read_field(ctx, &field, 0);
764 if (retval == 1) {
765 /* EOF marker */
766 retval = 0;
767 goto out;
768 } else if (retval != 0) {
769 /* read error */
770 goto out;
771 } else if (field->field_type == PWS3_RECORD_FIELD_END) {
772 /* empty record */
773 retval = -1;
774 goto out;
775 }
776
777 record = pws3_record_create();
778 if (record == NULL) {
779 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
780 errno, NULL);
781 goto out;
782 }
783
784 pws3_record_set_field(record, field);
785 field = NULL;
786
787 /* read the remaining fileds */
788 for (;;) {
789 retval = read_field(ctx, &field, 0);
790 if (retval == 1) {
791 /* end of record */
792 break;
793 } else if (retval != 0) {
794 /* read error */
795 retval = -1;
796 goto out;
797 }
798 pws3_record_set_field(record, field);
799 field = NULL;
800 }
801
802 /* check whether UUID is not empty */
803 if (pws3_record_get_field(record, PWS3_RECORD_FIELD_UUID) ==
804 NULL) {
805 /* record is missing mandatory fields */
806 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_RECORD,
807 "record is missing mandatory fields");
808 pws3_record_destroy(record);
809 retval = -1;
810 goto out;
811 }
812
813 pws3_file_insert_record(ctx->pws_file, record);
814 record = NULL;
815 }
816
817 out:
818 if (retval != 0) {
819 pws3_field_destroy(field);
820 pws3_record_destroy(record);
821 }
822
823 return (retval);
824 }
825
826 static int
827 verify_checksum(struct pws_file_ctx *ctx)
828 {
829 int retval;
830 unsigned char hmac_file[SHA256_DIGEST_SIZE];
831 unsigned char hmac[SHA256_DIGEST_SIZE];
832
833 retval = read_buf(ctx, hmac_file, sizeof (hmac_file));
834 if (retval == 1) {
835 pws_set_error(ctx->pws_file, PWS_ERR_TRUNCATED_FILE,
836 "unexpected end of file");
837 return (-1);
838 } else if (retval != 0) {
839 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
840 NULL);
841 return (-1);
842 }
843
844 hmac_sha256_digest(&ctx->hmac_ctx, sizeof (hmac), hmac);
845 if (memcmp(hmac_file, hmac, sizeof (hmac_file)) != 0) {
846 /* inconsistent database */
847 pws_set_error(ctx->pws_file, PWS_ERR_INVALID_CHECKSUM,
848 "checksum failed");
849 return (-1);
850 }
851
852 return (0);
853 }
854
855 static int
856 pws3_file_read(struct pws3_file *pws_file, const char *password,
857 unsigned char *s, size_t n, FILE *fp)
858 {
859 int retval = -1;
860 struct pws_file_ctx ctx = {
861 .fp = fp,
862 .mem = s,
863 .mem_size = n,
864 .mem_pos = 0,
865 .pws_file = pws_file,
866 };
867
868 pws_file_clear(pws_file);
869
870 retval = read_metadata(&ctx, password);
871 if (retval != 0) {
872 goto out;
873 }
874
875 retval = read_header(&ctx);
876 if (retval != 0) {
877 goto out;
878 }
879
880 retval = read_records(&ctx);
881 if (retval != 0) {
882 goto out;
883 }
884
885 retval = verify_checksum(&ctx);
886 if (retval != 0) {
887 goto out;
888 }
889
890 out:
891 if (retval != 0) {
892 pws_file_clear(ctx.pws_file);
893 }
894
895 return (retval);
896 }
897
898 int
899 pws3_file_read_mem(struct pws3_file *pws_file, const char *password,
900 unsigned char *s, size_t n)
901 {
902 return (pws3_file_read(pws_file, password, s, n, NULL));
903 }
904
905 int
906 pws3_file_read_stream(struct pws3_file *pws_file, const char *password,
907 FILE *fp)
908 {
909 return (pws3_file_read(pws_file, password, NULL, 0, fp));
910 }
911
912 static int
913 write_metadata(struct pws_file_ctx *ctx, const char *password)
914 {
915 int retval = -1;
916 unsigned char *stretched_key = NULL;
917 unsigned char *key_k = NULL;
918 unsigned char *key_l = NULL;
919 unsigned char metadata[METADATA_SIZE];
920 unsigned char *p = metadata;
921 unsigned char *salt;
922 uint32_t n_iter_le;
923 struct sha256_ctx md_ctx;
924 unsigned char *b1;
925 unsigned char *b3;
926 unsigned char *iv;
927 struct twofish_ctx cipher_ctx;
928
929 stretched_key = pws_secure_alloc(SHA256_DIGEST_SIZE);
930 if (stretched_key == NULL) {
931 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
932 "out of memory");
933 goto out;
934 }
935
936 key_k = pws_secure_alloc(KEY_SIZE);
937 if (key_k == NULL) {
938 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
939 "out of memory");
940 goto out;
941 }
942
943 key_l = pws_secure_alloc(KEY_SIZE);
944 if (key_l == NULL) {
945 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
946 "out of memory");
947 goto out;
948 }
949
950 /* generate new keys */
951 if (pws_random_bytes(key_k, KEY_SIZE) != 0) {
952 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
953 "failed to generate key");
954 goto out;
955 }
956
957 if (pws_random_bytes(key_l, KEY_SIZE) != 0) {
958 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
959 "failed to generate key");
960 goto out;
961 }
962
963 /* tag */
964 memcpy(p, psafe3_tag, sizeof (psafe3_tag));
965 p += sizeof (psafe3_tag);
966
967 /* generate new salt */
968 salt = p;
969 if (pws_random_bytes(salt, SALT_SIZE) != 0) {
970 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
971 "failed to generate salt");
972 goto out;
973 }
974 p += SALT_SIZE;
975
976 /* number of iterations */
977 n_iter_le = htole32(ctx->n_iter);
978 memcpy(p, &n_iter_le, 4);
979 p += 4;
980
981 /* stretch, hash password */
982 stretch_key(stretched_key, ctx->n_iter, password, strlen(password),
983 salt, SALT_SIZE);
984 sha256_init(&md_ctx);
985 sha256_update(&md_ctx, SHA256_DIGEST_SIZE, stretched_key);
986 sha256_digest(&md_ctx, SHA256_DIGEST_SIZE, p);
987 p += SHA256_DIGEST_SIZE;
988
989 b1 = p;
990 p += KEY_SIZE;
991
992 b3 = p;
993 p += KEY_SIZE;
994
995 /* generate IV */
996 iv = p;
997 if (pws_random_bytes(iv, TWOFISH_BLOCK_SIZE) != 0) {
998 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
999 "failed to generate IV");
1000 goto out;
1001 }
1002
1003 /* encrypt keys */
1004 twofish_set_key(&cipher_ctx, KEY_SIZE, stretched_key);
1005 twofish_encrypt(&cipher_ctx, KEY_SIZE, b1, key_k);
1006 twofish_encrypt(&cipher_ctx, KEY_SIZE, b3, key_l);
1007
1008 /* set key for decryption */
1009 twofish_set_key(&ctx->cipher_ctx.ctx, KEY_SIZE, key_k);
1010
1011 /* set IV */
1012 CBC_SET_IV(&ctx->cipher_ctx, p);
1013
1014 /* set key for HMAC */
1015 hmac_sha256_set_key(&ctx->hmac_ctx, KEY_SIZE, key_l);
1016
1017 /* write metadata */
1018 if (write_buf(ctx, metadata, sizeof (metadata)) != 0) {
1019 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1020 NULL);
1021 retval = -1;
1022 goto out;
1023 }
1024
1025 retval = 0;
1026
1027 out:
1028 pws_secure_free(key_k, (key_k != NULL) ? KEY_SIZE : 0);
1029 pws_secure_free(key_l, (key_l != NULL) ? KEY_SIZE : 0);
1030 pws_secure_free(stretched_key, (stretched_key != NULL) ?
1031 SHA256_DIGEST_SIZE : 0);
1032
1033 return (retval);
1034 }
1035
1036 static int
1037 write_block(struct pws_file_ctx *ctx, unsigned char *block)
1038 {
1039 unsigned char buf[TWOFISH_BLOCK_SIZE];
1040
1041 CBC_ENCRYPT(&ctx->cipher_ctx, twofish_encrypt, sizeof (buf), buf,
1042 block);
1043
1044 if (write_buf(ctx, buf, sizeof (buf)) != 0) {
1045 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1046 NULL);
1047 return (-1);
1048 }
1049
1050 return (0);
1051 }
1052
1053 static int
1054 write_field(struct pws_file_ctx *ctx, struct pws3_field *field)
1055 {
1056 int retval = -1;
1057 unsigned char *buf = NULL;
1058 unsigned char *p;
1059 unsigned char *field_data;
1060 enum pws_data_type data_type;
1061 size_t blocks = (field->size + 4 + 1) / TWOFISH_BLOCK_SIZE +
1062 ((field->size + 4 + 1) % TWOFISH_BLOCK_SIZE != 0);
1063 size_t i;
1064 size_t j;
1065 uint32_t len_le;
1066
1067 buf = pws_secure_alloc(TWOFISH_BLOCK_SIZE);
1068 if (buf == NULL) {
1069 pws_set_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
1070 "out of memory");
1071 goto out;
1072 }
1073
1074 data_type = pws3_field_get_data_type(field);
1075
1076 for (i = 0, j = 0; i < blocks; i++) {
1077 p = field_data = buf;
1078 if (pws_random_bytes(buf, TWOFISH_BLOCK_SIZE) != 0) {
1079 pws_set_error(ctx->pws_file, PWS_ERR_GENERIC_ERROR,
1080 "could not get random numbers");
1081 goto out;
1082 }
1083
1084 /* the first block of the field contains the length and type */
1085 if (i == 0) {
1086 len_le = htole32(field->size);
1087 memcpy(p, &len_le, 4);
1088 p += 4;
1089
1090 *p = field->field_type;
1091 p++;
1092 field_data = p;
1093 }
1094
1095 while ((j < field->size) &&
1096 (p - buf < (ptrdiff_t)TWOFISH_BLOCK_SIZE)) {
1097 switch (data_type) {
1098 case PWS_DATA_TYPE_UINT8:
1099 *p = field->value.uint8;
1100 break;
1101 case PWS_DATA_TYPE_UINT16:
1102 /* little endian */
1103 *p = (field->value.uint16 >> (8 * j)) & 0xff;
1104 break;
1105 case PWS_DATA_TYPE_TIME: /* FALLTHROUGH */
1106 case PWS_DATA_TYPE_UINT32:
1107 /* little endian */
1108 *p = (field->value.uint32 >> (8 * j)) & 0xff;
1109 break;
1110 case PWS_DATA_TYPE_TEXT:
1111 *p = field->value.text[j];
1112 break;
1113 case PWS_DATA_TYPE_UUID:
1114 *p = field->value.uuid[j];
1115 break;
1116 default:
1117 *p = field->value.bytes[j];
1118 }
1119
1120 p++;
1121 j++;
1122 }
1123
1124 hmac_sha256_update(&ctx->hmac_ctx, p - field_data, field_data);
1125
1126 retval = write_block(ctx, buf);
1127 if (retval != 0) {
1128 goto out;
1129 }
1130 }
1131
1132 retval = 0;
1133
1134 out:
1135 pws_secure_free(buf, (buf != NULL) ? TWOFISH_BLOCK_SIZE : 0);
1136
1137 return (retval);
1138 }
1139
1140 static int
1141 write_header(struct pws_file_ctx *ctx)
1142 {
1143 int retval = -1;
1144 size_t i;
1145 struct pws3_field *version_field;
1146 struct pws3_field *field = NULL;
1147 struct pws3_field *end_field = NULL;
1148
1149 version_field = pws3_file_get_header_field(ctx->pws_file,
1150 PWS3_HEADER_FIELD_VERSION);
1151 if (version_field == NULL) {
1152 /* add mandatory version header version_field if necessary */
1153 version_field = pws3_field_create(1, PWS3_HEADER_FIELD_VERSION);
1154 if (version_field == NULL) {
1155 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY,
1156 errno, NULL);
1157 goto out;
1158 }
1159 pws3_field_set_uint16(version_field, PWS3_VERSION);
1160 pws3_file_set_header_field(ctx->pws_file, version_field);
1161 }
1162 retval = write_field(ctx, version_field);
1163 if (retval != 0) {
1164 goto out;
1165 }
1166
1167 for (i = 0x01; i < 0xff; i++) {
1168 if (ctx->pws_file->fields[i] != NULL) {
1169 retval = write_field(ctx, ctx->pws_file->fields[i]);
1170 if (retval != 0) {
1171 goto out;
1172 }
1173 }
1174 }
1175
1176 RB_FOREACH(field, empty_groups_tree, ctx->pws_file->empty_groups_tree) {
1177 retval = write_field(ctx, field);
1178 if (retval != 0) {
1179 goto out;
1180 }
1181 }
1182
1183 end_field = pws3_field_create(1, PWS3_HEADER_FIELD_END);
1184 retval = write_field(ctx, end_field);
1185 if (retval != 0) {
1186 goto out;
1187 }
1188
1189 out:
1190 pws3_field_destroy(end_field);
1191
1192 return (retval);
1193 }
1194
1195 static int
1196 write_records(struct pws_file_ctx *ctx)
1197 {
1198 int retval = -1;
1199 struct pws3_field *end_field = NULL;
1200 size_t i;
1201 struct pws3_record *record;
1202
1203 end_field = pws3_field_create(0, PWS3_RECORD_FIELD_END);
1204 if (end_field == NULL) {
1205 pws_set_system_error(ctx->pws_file, PWS_ERR_NO_MEMORY, errno,
1206 NULL);
1207 goto out;
1208 }
1209
1210 RB_FOREACH(record, records_tree, ctx->pws_file->records_tree) {
1211 /* record fields */
1212 for (i = 0x01; i < 0xff; i++) {
1213 if (record->fields[i] != NULL) {
1214 retval = write_field(ctx, record->fields[i]);
1215 if (retval != 0) {
1216 goto out;
1217 }
1218 }
1219 }
1220
1221 /* end of entry marker */
1222 retval = write_field(ctx, end_field);
1223 if (retval != 0) {
1224 goto out;
1225 }
1226 }
1227
1228 /* end of file marker */
1229 if (write_buf(ctx, eof_marker, sizeof (eof_marker)) != 0) {
1230 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1231 NULL);
1232 retval = -1;
1233 goto out;
1234 }
1235
1236 retval = 0;
1237
1238 out:
1239 pws3_field_destroy(end_field);
1240
1241 return (retval);
1242 }
1243
1244 static int
1245 write_checksum(struct pws_file_ctx *ctx)
1246 {
1247 unsigned char hmac[SHA256_DIGEST_SIZE];
1248
1249 hmac_sha256_digest(&ctx->hmac_ctx, sizeof (hmac), hmac);
1250
1251 if (write_buf(ctx, hmac, sizeof (hmac)) != 0) {
1252 pws_set_system_error(ctx->pws_file, PWS_ERR_IO_ERROR, errno,
1253 NULL);
1254 return (-1);
1255 }
1256
1257 return (0);
1258 }
1259
1260 static int
1261 pws3_file_write(struct pws3_file *pws_file, const char *password,
1262 uint32_t n_iter, unsigned char **memp, size_t *mem_sizep, FILE *fp)
1263 {
1264 int retval = -1;
1265 struct pws_file_ctx ctx = {
1266 .fp = fp,
1267 .pws_file = pws_file,
1268 .n_iter = n_iter
1269 };
1270
1271 retval = write_metadata(&ctx, password);
1272 if (retval != 0) {
1273 goto out;
1274 }
1275
1276 retval = write_header(&ctx);
1277 if (retval != 0) {
1278 goto out;
1279 }
1280
1281 retval = write_records(&ctx);
1282 if (retval != 0) {
1283 goto out;
1284 }
1285
1286 retval = write_checksum(&ctx);
1287 if (retval != 0) {
1288 goto out;
1289 }
1290
1291 if (memp != NULL) {
1292 *memp = ctx.mem;
1293 *mem_sizep = ctx.mem_size;
1294 }
1295
1296 out:
1297 if (retval != 0) {
1298 pws_free(ctx.mem, ctx.mem_size);
1299 }
1300
1301 return (retval);
1302 }
1303
1304 int
1305 pws3_file_write_mem(struct pws3_file *pws_file, const char *password,
1306 uint32_t n_iter, unsigned char **memp, size_t *mem_sizep)
1307 {
1308 PWS_ASSERT(memp != NULL);
1309 PWS_ASSERT(mem_sizep != NULL);
1310
1311 return (pws3_file_write(pws_file, password, n_iter, memp, mem_sizep,
1312 NULL));
1313 }
1314
1315 int
1316 pws3_file_write_stream(struct pws3_file *pws_file, const char *password,
1317 uint32_t n_iter, FILE *fp)
1318 {
1319 PWS_ASSERT(fp != NULL);
1320
1321 return (pws3_file_write(pws_file, password, n_iter, NULL, NULL, fp));
1322 }
1323
1324 void
1325 pws3_file_set_header_field(struct pws3_file *pws_file, struct pws3_field *field)
1326 {
1327 PWS_ASSERT(pws3_field_is_header(field));
1328 PWS_ASSERT((pws3_field_get_data_type(field) != PWS_DATA_TYPE_TEXT) ||
1329 (field->value.text != NULL));
1330 PWS_ASSERT((pws3_field_get_data_type(field) != PWS_DATA_TYPE_BYTES) ||
1331 (field->value.bytes != NULL));
1332
1333 if (field->field_type == PWS3_HEADER_FIELD_EMPTY_GROUPS) {
1334 pws3_file_insert_empty_group(pws_file, field);
1335 return;
1336 }
1337
1338 pws3_field_destroy(pws3_file_remove_header_field(pws_file,
1339 field->field_type));
1340 pws_file->fields[field->field_type] = field;
1341 }
1342
1343 struct pws3_field *
1344 pws3_file_get_header_field(struct pws3_file *pws_file, uint8_t field_type)
1345 {
1346 if (field_type == PWS3_HEADER_FIELD_EMPTY_GROUPS) {
1347 return (pws3_file_first_empty_group(pws_file));
1348 }
1349
1350 return (pws_file->fields[field_type]);
1351 }
1352
1353 struct pws3_field *
1354 pws3_file_remove_header_field(struct pws3_file *pws_file, uint8_t field_type)
1355 {
1356 struct pws3_field *field;
1357
1358 if (field_type == PWS3_HEADER_FIELD_EMPTY_GROUPS) {
1359 return (NULL);
1360 }
1361
1362 field = pws3_file_get_header_field(pws_file, field_type);
1363 pws_file->fields[field_type] = NULL;
1364
1365 return (field);
1366 }
1367
1368 void
1369 pws3_file_insert_empty_group(struct pws3_file *pws_file,
1370 struct pws3_field *field)
1371 {
1372 const char *group_name;
1373
1374 PWS_ASSERT(pws3_field_is_header(field));
1375 PWS_ASSERT(pws3_field_get_type(field) ==
1376 PWS3_HEADER_FIELD_EMPTY_GROUPS);
1377
1378 group_name = pws3_field_get_text(field);
1379 pws3_field_destroy(pws3_file_remove_empty_group(pws_file, group_name));
1380 RB_INSERT(empty_groups_tree, pws_file->empty_groups_tree, field);
1381 }
1382
1383 struct pws3_field *
1384 pws3_file_get_empty_group(struct pws3_file *pws_file, const char *group_name)
1385 {
1386 return (RB_FIND(empty_groups_tree, pws_file->empty_groups_tree,
1387 (&(struct pws3_field){ .is_header = 1,
1388 .field_type = PWS3_HEADER_FIELD_EMPTY_GROUPS,
1389 .value.text = (char *)group_name })));
1390 }
1391
1392 struct pws3_field *
1393 pws3_file_remove_empty_group(struct pws3_file *pws_file, const char *group_name)
1394 {
1395 struct pws3_field *field;
1396
1397 field = RB_FIND(empty_groups_tree, pws_file->empty_groups_tree,
1398 (&(struct pws3_field){ .is_header = 1,
1399 .field_type = PWS3_HEADER_FIELD_EMPTY_GROUPS,
1400 .value.text = (char *)group_name }));
1401 if (field != NULL) {
1402 RB_REMOVE(empty_groups_tree, pws_file->empty_groups_tree,
1403 field);
1404 }
1405
1406 return (field);
1407 }
1408
1409 struct pws3_field *
1410 pws3_file_first_empty_group(struct pws3_file *pws_file)
1411 {
1412 return (RB_MIN(empty_groups_tree, pws_file->empty_groups_tree));
1413 }
1414
1415 struct pws3_field *
1416 pws3_file_last_empty_group(struct pws3_file *pws_file)
1417 {
1418 return (RB_MAX(empty_groups_tree, pws_file->empty_groups_tree));
1419 }
1420
1421 struct pws3_field *
1422 pws3_file_next_empty_group(struct pws3_file *pws_file, struct pws3_field *field)
1423 {
1424 return (RB_NEXT(empty_groups_tree, pws_file->empty_groups_tree, field));
1425 }
1426
1427 struct pws3_field *
1428 pws3_file_prev_empty_group(struct pws3_file *pws_file, struct pws3_field *field)
1429 {
1430 return (RB_PREV(empty_groups_tree, pws_file->empty_groups_tree, field));
1431 }
1432
1433 void
1434 pws3_file_insert_record(struct pws3_file *pws_file, struct pws3_record *record)
1435 {
1436 struct pws3_field *uuid_field;
1437 const unsigned char *uuid;
1438
1439 uuid_field = pws3_record_get_field(record, PWS3_RECORD_FIELD_UUID);
1440 PWS_ASSERT(uuid_field != NULL);
1441 uuid = pws3_field_get_uuid(uuid_field);
1442
1443 /* replace existing record */
1444 pws3_record_destroy(pws3_file_remove_record(pws_file, uuid));
1445
1446 RB_INSERT(records_tree, pws_file->records_tree, record);
1447 }
1448
1449 struct pws3_record *
1450 pws3_file_get_record(struct pws3_file *pws_file,
1451 const unsigned char uuid[static PWS3_UUID_SIZE])
1452 {
1453 struct pws3_field uuid_field = {
1454 .is_header = 0,
1455 .field_type = PWS3_RECORD_FIELD_UUID
1456 };
1457 struct pws3_record search_record = {
1458 .fields[PWS3_RECORD_FIELD_UUID] = &uuid_field
1459 };
1460
1461 memcpy(uuid_field.value.uuid, uuid, PWS3_UUID_SIZE);
1462
1463 return (RB_FIND(records_tree, pws_file->records_tree, &search_record));
1464 }
1465
1466 struct pws3_record *
1467 pws3_file_remove_record(struct pws3_file *pws_file,
1468 const unsigned char uuid[static PWS3_UUID_SIZE])
1469 {
1470 struct pws3_record *record;
1471
1472 record = pws3_file_get_record(pws_file, uuid);
1473 if (record != NULL) {
1474 RB_REMOVE(records_tree, pws_file->records_tree, record);
1475 }
1476
1477 return (record);
1478 }
1479
1480 struct pws3_record *
1481 pws3_file_first_record(struct pws3_file *pws_file)
1482 {
1483 return (RB_MIN(records_tree, pws_file->records_tree));
1484 }
1485
1486 struct pws3_record *
1487 pws3_file_last_record(struct pws3_file *pws_file)
1488 {
1489 return (RB_MAX(records_tree, pws_file->records_tree));
1490 }
1491
1492 struct pws3_record *
1493 pws3_file_next_record(struct pws3_file *pws_file, struct pws3_record *record)
1494 {
1495 return (RB_NEXT(records_tree, pws_file->records_tree, record));
1496 }
1497
1498 struct pws3_record *
1499 pws3_file_prev_record(struct pws3_file *pws_file, struct pws3_record *record)
1500 {
1501 return (RB_PREV(records_tree, pws_file->records_tree, record));
1502 }