Mercurial > projects > libpws
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 } |