1 /* keepass2john utility (modified KeeCracker) written in March of 2012
2  * by Dhiru Kholia. keepass2john processes input KeePass 1.x and 2.x
3  * database files into a format suitable for use with JtR. This software
4  * is Copyright (c) 2012, Dhiru Kholia <dhiru.kholia at gmail.com> and it
5  * is hereby released under GPL license.
6  *
7  * KeePass 2.x support is based on KeeCracker - The KeePass 2 Database
8  * Cracker, http://keecracker.mbw.name/
9  *
10  * KeePass 1.x support is based on kppy -  A Python-module to provide
11  * an API to KeePass 1.x files. https://github.com/raymontag/kppy
12  * Copyright (C) 2012 Karsten-Kai König <kkoenig@posteo.de>
13  *
14  * Keyfile support for Keepass 1.x and Keepass 2.x was added by Fist0urs
15  * <eddy.maaalou at gmail.com>
16  *
17  * kppy is free software: you can redistribute it and/or modify it under the terms
18  * of the GNU General Public License as published by the Free Software Foundation,
19  * either version 3 of the License, or at your option) any later version.
20  *
21  * kppy is distributed in the hope that it will be useful, but WITHOUT ANY
22  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
23  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License along with
26  * kppy. If not, see <http://www.gnu.org/licenses/>. */
27 
28 #if AC_BUILT
29 #include "autoconfig.h"
30 #endif
31 
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <stdint.h>
37 #ifdef _MSC_VER
38 #include "missing_getopt.h"
39 #endif
40 #include <errno.h>
41 // needs to be above sys/types.h and sys/stat.h for mingw, if -std=c99 used.
42 #include "jumbo.h"
43 #include <sys/stat.h>
44 #include <sys/types.h>
45 #if  (!AC_BUILT || HAVE_UNISTD_H) && !_MSC_VER
46 #include <unistd.h>	// getopt defined here for unix
47 #endif
48 #include "params.h"
49 #include "memory.h"
50 
51 #include "sha2.h"
52 #include "base64_convert.h"
53 
54 const char *extension[] = {".kdbx"};
55 static char *keyfile = NULL;
56 
57 // KeePass 1.x signature
58 uint32_t FileSignatureOld1 = 0x9AA2D903;
59 uint32_t FileSignatureOld2 = 0xB54BFB65;
60 /// <summary>
61 /// File identifier, first 32-bit value.
62 /// </summary>
63 uint32_t FileSignature1 = 0x9AA2D903;
64 /// <summary>
65 /// File identifier, second 32-bit value.
66 /// </summary>
67 uint32_t FileSignature2 = 0xB54BFB67;
68 // KeePass 2.x pre-release (alpha and beta) signature
69 uint32_t FileSignaturePreRelease1 = 0x9AA2D903;
70 uint32_t FileSignaturePreRelease2 = 0xB54BFB66;
71 uint32_t FileVersionCriticalMask = 0xFFFF0000;
72 /// <summary>
73 /// File version of files saved by the current <c>Kdb4File</c> class.
74 /// KeePass 2.07 has version 1.01, 2.08 has 1.02, 2.09 has 2.00,
75 /// 2.10 has 2.02, 2.11 has 2.04, 2.15 has 3.00.
76 /// The first 2 bytes are critical (i.e. loading will fail, if the
77 /// file version is too high), the last 2 bytes are informational.
78 /// </summary>
79 // uint32_t FileVersion32 = 0x00030000;
80 uint32_t FileVersion32 = 0x00040000;
81 uint32_t FileVersion32_4 = 0x00040000;  // from KeePass 2.36 sources
82 
83 // We currently support database formats up to KDBX v3.x. KDBX 4.x is not
84 // supported yet. See "KdbxFile.cs" in KeePass 2.36 for more information on
85 // KDBX 4.x format.
86 
87 enum Kdb4HeaderFieldID
88 {
89 	EndOfHeader = 0,
90 	CipherID = 2,
91 	MasterSeed = 4,
92 	TransformSeed = 5,  // KDBX 3.1, for backward compatibility only
93 	TransformRounds = 6,  // KDBX 3.1, for backward compatibility only
94 	EncryptionIV = 7,
95 	StreamStartBytes = 9,  // KDBX 3.1, for backward compatibility only
96 	KdfParameters = 11,  // KDBX 4, superseding Transform*
97 };
98 
get_file_size(char * filename)99 static off_t get_file_size(char * filename)
100 {
101 	struct stat sb;
102 	if (stat(filename, & sb) != 0) {
103 		fprintf(stderr, "! %s : stat failed, %s\n", filename, strerror(errno));
104 		exit(-2);
105 	}
106 	return sb.st_size;
107 }
108 
print_hex(unsigned char * str,int len)109 static void print_hex(unsigned char *str, int len)
110 {
111 	int i;
112 	for (i = 0; i < len; ++i)
113 		printf("%02x", str[i]);
114 }
115 
BytesToUInt64(unsigned char * s,const int s_size)116 static uint64_t BytesToUInt64(unsigned char * s, const int s_size)
117 {
118 	int i;
119 	uint64_t v = 0;
120 
121 	for (i = 0; i < 8 && i < s_size; i++)
122 		v |= (uint64_t)s[i] << 8 * i;
123 	return v;
124 }
125 
fget32(FILE * fp)126 static uint32_t fget32(FILE * fp)
127 {
128 	uint32_t v = (uint32_t)fgetc(fp);
129 	v |= (uint32_t)fgetc(fp) << 8;
130 	v |= (uint32_t)fgetc(fp) << 16;
131 	v |= (uint32_t)fgetc(fp) << 24;
132 	return v;
133 }
134 
fget16(FILE * fp)135 static uint16_t fget16(FILE * fp)
136 {
137 	uint32_t v = fgetc(fp);
138 	v |= fgetc(fp) << 8;
139 	return v;
140 }
141 
warn(const char * fmt,...)142 static void warn(const char *fmt, ...)
143 {
144 	va_list ap;
145 
146 	va_start(ap, fmt);
147 	if (fmt != NULL)
148 		vfprintf(stderr, fmt, ap);
149 	va_end(ap);
150 	fprintf(stderr, "\n");
151 
152 	// exit(EXIT_FAILURE);
153 }
154 
155 /* process KeePass 1.x databases */
process_old_database(FILE * fp,char * encryptedDatabase)156 static void process_old_database(FILE *fp, char* encryptedDatabase)
157 {
158 	uint32_t enc_flag;
159 	uint32_t version;
160 	unsigned char final_randomseed[16];
161 	unsigned char enc_iv[16];
162 	unsigned char contents_hash[32];
163 	unsigned char transf_randomseed[32];
164 	uint32_t num_groups;
165 	uint32_t num_entries;
166 	uint32_t key_transf_rounds;
167 	unsigned char *buffer;
168 	int64_t filesize = 0;
169 	int64_t datasize;
170 	int algorithm = -1;
171 	char *dbname;
172 	FILE *kfp = NULL;
173 
174 	/* specific to keyfile handling */
175 	int64_t filesize_keyfile = 0;
176 	SHA256_CTX ctx;
177 	unsigned char hash[32];
178 	int counter;
179 
180 	enc_flag = fget32(fp);
181 	version = fget32(fp);
182 
183 	if (fread(final_randomseed, 16, 1, fp) != 1) {
184 		warn("%s: Error: read failed: %s.", encryptedDatabase,
185 			strerror(errno));
186 		return;
187 	}
188 	if (fread(enc_iv, 16, 1, fp) != 1) {
189 		warn("%s: Error: read failed: %s.", encryptedDatabase,
190 			strerror(errno));
191 		return;
192 	}
193 
194 	num_groups = fget32(fp);
195 	num_entries = fget32(fp);
196 	(void)num_groups;
197 	(void)num_entries;
198 
199 	if (fread(contents_hash, 32, 1, fp) != 1) {
200 		warn("%s: Error: read failed: %s.", encryptedDatabase,
201 			strerror(errno));
202 		return;
203 	}
204 	if (fread(transf_randomseed, 32, 1, fp) != 1) {
205 		warn("%s: Error: read failed: %s.", encryptedDatabase,
206 			strerror(errno));
207 		return;
208 	}
209 
210 	key_transf_rounds = fget32(fp);
211 	/* Check if the database is supported */
212 	if ((version & 0xFFFFFF00) != (0x00030002 & 0xFFFFFF00)) {
213 		fprintf(stderr, "! %s : Unsupported file version (%u)!\n", encryptedDatabase, version);
214 		return;
215 	}
216 	/* src/Kdb3Database.cpp from KeePass 0.4.3 is authoritative */
217 	if (enc_flag & 2) {
218 		algorithm = 0; // AES
219 	} else if (enc_flag & 8) {
220 		algorithm = 1; // Twofish
221 	} else {
222 		fprintf(stderr, "! %s : Unsupported file encryption (%u)!\n", encryptedDatabase, enc_flag);
223 		return;
224 	}
225 
226 	/* keyfile processing */
227 	if (keyfile) {
228 		kfp = fopen(keyfile, "rb");
229 		if (!kfp) {
230 			fprintf(stderr, "! %s : %s\n", keyfile, strerror(errno));
231 			return;
232 		}
233 		filesize_keyfile = (int64_t)get_file_size(keyfile);
234 	}
235 
236 	dbname = strip_suffixes(basename(encryptedDatabase), extension, 1);
237 	filesize = (int64_t)get_file_size(encryptedDatabase);
238 	datasize = filesize - 124;
239 	if (datasize < 0) {
240 		warn("%s: Error in validating datasize.", encryptedDatabase);
241 		return;
242 	}
243 	// offset (124) field below is not used, we hijack it to convey the
244 	// algorithm.
245 	// printf("%s:$keepass$*1*%d*%d*", dbname, key_transf_rounds, 124);
246 	printf("%s:$keepass$*1*%d*%d*", dbname, key_transf_rounds, algorithm);
247 	print_hex(final_randomseed, 16);
248 	printf("*");
249 	print_hex(transf_randomseed, 32);
250 	printf("*");
251 	print_hex(enc_iv, 16);
252 	printf("*");
253 	print_hex(contents_hash, 32);
254 
255 	buffer = (unsigned char*)malloc(datasize * sizeof(char));
256 
257 	/* we inline the content with the hash */
258 	fprintf(stderr, "Inlining %s\n", encryptedDatabase);
259 	printf("*1*%"PRId64"*", datasize);
260 	fseek(fp, 124, SEEK_SET);
261 	if (fread(buffer, datasize, 1, fp) != 1) {
262 		warn("%s: Error: read failed: %s.",
263 		          encryptedDatabase, strerror(errno));
264 		MEM_FREE(buffer);
265 		return;
266 	}
267 
268 	print_hex(buffer, datasize);
269 	MEM_FREE(buffer);
270 
271 	if (keyfile) {
272 		buffer = (unsigned char*)malloc(filesize_keyfile * sizeof(char));
273 		printf("*1*64*"); /* inline keyfile content */
274 		if (fread(buffer, filesize_keyfile, 1, kfp) != 1) {
275 			warn("%s: Error: read failed: %s.",
276 				encryptedDatabase, strerror(errno));
277 			return;
278 		}
279 
280 		/* as in Keepass 1.x implementation:
281 		 *  if filesize_keyfile == 32 then assume byte_array
282 		 *  if filesize_keyfile == 64 then assume hex(byte_array)
283 		 *  else byte_array = sha256(keyfile_content)
284 		 */
285 
286 		if (filesize_keyfile == 32)
287 			print_hex(buffer, filesize_keyfile);
288 		else if (filesize_keyfile == 64){
289 			for (counter = 0; counter <64; counter++)
290 				printf("%c", buffer[counter]);
291 		}
292 		else{
293 		  /* precompute sha256 to speed-up cracking */
294 		  SHA256_Init(&ctx);
295 		  SHA256_Update(&ctx, buffer, filesize_keyfile);
296 		  SHA256_Final(hash, &ctx);
297 		  print_hex(hash, 32);
298 		}
299 		MEM_FREE(buffer);
300 	}
301 	printf("\n");
302 }
303 
304 // Synchronize with KdbxFile.Read.cs from KeePass 2.x
process_database(char * encryptedDatabase)305 static void process_database(char* encryptedDatabase)
306 {
307 	// long dataStartOffset;
308 	unsigned long transformRounds = 0;
309 	unsigned char *masterSeed = NULL;
310 	int masterSeedLength = 0;
311 	unsigned char *transformSeed = NULL;
312 	int transformSeedLength = 0;
313 	unsigned char *initializationVectors = NULL;
314 	int initializationVectorsLength = 0;
315 	unsigned char *expectedStartBytes = NULL;
316 	int endReached, expectedStartBytesLength = 0;
317 	uint32_t uSig1, uSig2, uVersion;
318 	FILE *fp;
319 	unsigned char out[32];
320 	char *dbname;
321 	long algorithm = 0;  // 0 -> AES
322 	size_t fsize = 0;
323 
324 	/* specific to keyfile handling */
325 	unsigned char *buffer;
326 	int64_t filesize_keyfile = 0;
327 	char *p;
328 	char *data;
329 	char b64_decoded[128+1];
330 	FILE *kfp = NULL;
331 	SHA256_CTX ctx;
332 	unsigned char hash[32];
333 	int counter;
334 
335 	fp = fopen(encryptedDatabase, "rb");
336 	if (!fp) {
337 		fprintf(stderr, "! %s : %s\n", encryptedDatabase, strerror(errno));
338 		return;
339 	}
340 	fseek(fp, 0, SEEK_END);
341 	fsize = ftell(fp);
342 	fseek(fp, 0, SEEK_SET);
343 	uSig1 = fget32(fp);
344 	uSig2 = fget32(fp);
345 	if ((uSig1 == FileSignatureOld1) && (uSig2 == FileSignatureOld2)) {
346 		process_old_database(fp, encryptedDatabase);
347 		fclose(fp);
348 		return;
349 	}
350 	if ((uSig1 == FileSignature1) && (uSig2 == FileSignature2)) {
351 	}
352 	else if ((uSig1 == FileSignaturePreRelease1) && (uSig2 == FileSignaturePreRelease2)) {
353 	}
354 	else {
355 		fprintf(stderr, "! %s : Unknown format: File signature invalid\n", encryptedDatabase);
356 		fclose(fp);
357 		return;
358 	}
359 	uVersion = fget32(fp);
360 	if ((uVersion & FileVersionCriticalMask) > (FileVersion32 & FileVersionCriticalMask)) {
361 		fprintf(stderr, "! %s : Unknown format: File version '%x' unsupported\n", encryptedDatabase, uVersion);
362 		fclose(fp);
363 		return;
364 	}
365 	endReached = 0;
366 	while (!endReached) {
367 		uint32_t uSize;
368 		unsigned char btFieldID = fgetc(fp);
369 		enum Kdb4HeaderFieldID kdbID = btFieldID;
370 		unsigned char *pbData = NULL;
371 
372 		if (uVersion < FileVersion32_4)
373 			uSize = fget16(fp);
374 		else
375 			uSize = fget32(fp);
376 
377 		if (fsize * 64 < uSize) {
378 			fprintf(stderr, "uSize too large, is the database corrupt?\n");
379 			goto bailout;
380 		}
381 		if (uSize == 0 && (kdbID != EndOfHeader)) {
382 			fprintf(stderr, "error validating uSize for EndOfHeader, is the database corrupt?\n");
383 			goto bailout;
384 		}
385 		if (uSize > 0) {
386 			pbData = (unsigned char*)malloc(uSize);
387 			if (!pbData || fread(pbData, uSize, 1, fp) != 1) {
388 				fprintf(stderr, "error allocating / reading pbData, is the database corrupt?\n");
389 				MEM_FREE(pbData);
390 				goto bailout;
391 			}
392 		}
393 		switch (kdbID)
394 		{
395 			case EndOfHeader:
396 				endReached = 1;  // end of header
397 				MEM_FREE(pbData);
398 				break;
399 
400 			case MasterSeed:
401 				if (masterSeed)
402 					MEM_FREE(masterSeed);
403 				masterSeed = pbData;
404 				masterSeedLength = uSize;
405 				break;
406 
407 			case TransformSeed: // Obsolete in FileVersion32_4; for backward compatibility only
408 				if (transformSeed)
409 					MEM_FREE(transformSeed);
410 
411 				transformSeed = pbData;
412 				transformSeedLength = uSize;
413 				break;
414 
415 			case TransformRounds:  // Obsolete in FileVersion32_4; for backward compatibility only
416 				if (uSize < 4) {
417 					fprintf(stderr, "error validating uSize for TransformRounds, is the database corrupt?\n");
418 					MEM_FREE(pbData);
419 					goto bailout;
420 				}
421 				if (!pbData) {
422 					fprintf(stderr, "! %s : parsing failed (pbData is NULL), please open a bug if target is valid KeepPass database.\n", encryptedDatabase);
423 					goto bailout;
424 				}
425 				else {
426 					transformRounds = BytesToUInt64(pbData, uSize);
427 					MEM_FREE(pbData);
428 				}
429 				break;
430 
431 			case EncryptionIV:
432 				if (initializationVectors)
433 					MEM_FREE(initializationVectors);
434 				initializationVectors = pbData;
435 				initializationVectorsLength = uSize;
436 				break;
437 
438 			case StreamStartBytes:  // Not present in FileVersion32_4
439 				if (expectedStartBytes)
440 					MEM_FREE(expectedStartBytes);
441 				expectedStartBytes = pbData;
442 				expectedStartBytesLength = uSize;
443 				break;
444 
445 			case CipherID:
446 				// pbData == 31c1f2e6bf714350be5805216afc5aff => AES ("Standard" KDBX 3.1)
447 				// pbData == d6038a2b8b6f4cb5a524339a31dbb59a => ChaCha20
448 				// pbData == ad68f29f576f4bb9a36ad47af965346c => TwoFish
449 				if (uSize < 4) {
450 					fprintf(stderr, "error validating uSize for CipherID, is the database corrupt?\n");
451 					MEM_FREE(pbData);
452 					goto bailout;
453 				}
454 				if (memcmp(pbData, "\xd6\x03\x8a\x2b", 4) == 0) {
455 					// fprintf(stderr, "! %s : ChaCha20 usage is not supported yet!\n", encryptedDatabase);
456 					// MEM_FREE(pbData);
457 					algorithm = 2;
458 					// goto bailout;
459 				}
460 				/* if (memcmp(pbData, "\x31\xc1\xf2\xe6", 4) != 0) {
461 					fprintf(stderr, "! %s : Unsupported CipherID found!\n", encryptedDatabase);
462 					MEM_FREE(pbData);
463 					goto bailout;
464 				} */
465 
466 			default:
467 				MEM_FREE(pbData);
468 				break;
469 		}
470 	}
471 	// dataStartOffset = ftell(fp);
472 	if (transformRounds == 0 && uVersion < FileVersion32_4) {
473 		fprintf(stderr, "! %s : transformRounds can't be 0\n", encryptedDatabase);
474 		goto bailout;
475 	}
476 #ifdef KEEPASS_DEBUG
477 	fprintf(stderr, "%d, %d, %d, %d\n", masterSeedLength, transformSeedLength, initializationVectorsLength, expectedStartBytesLength);
478 #endif
479 	if ((uVersion < FileVersion32_4) && (!masterSeed || !transformSeed || !initializationVectors || !expectedStartBytes)) {
480 		fprintf(stderr, "! %s : parsing failed, please open a bug if target is valid KeepPass database.\n", encryptedDatabase);
481 		goto bailout;
482 	}
483 
484 	if (uVersion >= FileVersion32_4) {
485 		fprintf(stderr, "! %s : File version '%x' is currently not supported!\n", encryptedDatabase, uVersion);
486 		goto bailout;
487 	}
488 
489 	if (keyfile) {
490 		kfp = fopen(keyfile, "rb");
491 		if (!kfp) {
492 			fprintf(stderr, "! %s : %s\n", keyfile, strerror(errno));
493 			return;
494 		}
495 		filesize_keyfile = (int64_t)get_file_size(keyfile);
496 	}
497 
498 	dbname = strip_suffixes(basename(encryptedDatabase),extension, 1);
499 	// printf("%s:$keepass$*2*%ld*%ld*", dbname, transformRounds, dataStartOffset);
500 	printf("%s:$keepass$*2*%ld*%ld*", dbname, transformRounds, algorithm);  // dataStartOffset field is now used to convey algorithm information
501 	print_hex(masterSeed, masterSeedLength);
502 	printf("*");
503 	print_hex(transformSeed, transformSeedLength);
504 	printf("*");
505 	print_hex(initializationVectors, initializationVectorsLength);
506 	printf("*");
507 	print_hex(expectedStartBytes, expectedStartBytesLength);
508 	if (fread(out, 32, 1, fp) != 1) {
509 		fprintf(stderr, "error reading encrypted data!\n");
510 		goto bailout;
511 	}
512 	printf("*");
513 	print_hex(out, 32);
514 
515 	if (keyfile) {
516 		buffer = (unsigned char*)malloc(filesize_keyfile * sizeof(char));
517 		printf("*1*64*"); /* inline keyfile content */
518 		if (fread(buffer, filesize_keyfile, 1, kfp) != 1) {
519 			warn("%s: Error: read failed: %s.",
520 				encryptedDatabase, strerror(errno));
521 			return;
522 		}
523 
524 		/* as in Keepass 2.x implementation:
525 		 *  if keyfile is an xml, get <Data> content
526 		 *  if filesize_keyfile == 32 then assume byte_array
527 		 *  if filesize_keyfile == 64 then assume hex(byte_array)
528 		 *  else byte_array = sha256(keyfile_content)
529 		 */
530 
531 		if (!memcmp((char *) buffer, "<?xml", 5)
532 			&& ((p = strstr((char *) buffer, "<Key>")) != NULL)
533 			&& ((p = strstr(p, "<Data>")) != NULL)
534 			)
535 		{
536 			p += strlen("<Data>");
537 			data = p;
538 			p = strstr(p, "</Data>");
539 			printf ("%s", base64_convert_cp(data, e_b64_mime, p - data, b64_decoded, e_b64_hex, sizeof(b64_decoded), flg_Base64_NO_FLAGS, 0));
540 		}
541 		else if (filesize_keyfile == 32)
542 			print_hex(buffer, filesize_keyfile);
543 		else if (filesize_keyfile == 64)
544 		{
545 			for (counter = 0; counter <64; counter++)
546 				printf("%c", buffer[counter]);
547 		}
548 		else
549 		{
550 		  /* precompute sha256 to speed-up cracking */
551 
552 		  SHA256_Init(&ctx);
553 		  SHA256_Update(&ctx, buffer, filesize_keyfile);
554 		  SHA256_Final(hash, &ctx);
555 		  print_hex(hash, 32);
556 		}
557 		MEM_FREE(buffer);
558 	}
559 	printf("\n");
560 
561 bailout:
562 	MEM_FREE(masterSeed);
563 	MEM_FREE(transformSeed);
564 	MEM_FREE(initializationVectors);
565 	MEM_FREE(expectedStartBytes);
566 	fclose(fp);
567 }
568 
569 #ifndef HAVE_LIBFUZZER
usage(char * name)570 static int usage(char *name)
571 {
572 	fprintf(stderr, "Usage: %s [-k <keyfile>] <.kdbx database(s)>\n", name);
573 
574 	return EXIT_FAILURE;
575 }
576 
main(int argc,char ** argv)577 int main(int argc, char **argv)
578 {
579 	int c;
580 
581 	errno = 0;
582 	/* Parse command line */
583 	while ((c = getopt(argc, argv, "k:")) != -1) {
584 		switch (c) {
585 		case 'k':
586 			keyfile = (char *)malloc(strlen(optarg) + 1);
587 			strcpy(keyfile, optarg);
588 			break;
589 		case '?':
590 		default:
591 			return usage(argv[0]);
592 		}
593 	}
594 	argc -= optind;
595 	if (argc == 0)
596 		return usage(argv[0]);
597 	argv += optind;
598 
599 	while(argc--)
600 		process_database(*argv++);
601 
602 	return 0;
603 }
604 #endif
605 
606 #ifdef HAVE_LIBFUZZER
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)607 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
608 {
609 	int fd;
610 	char name[] = "/tmp/libFuzzer-XXXXXX";
611 
612 	fd = mkstemp(name);  // this approach is somehow faster than the fmemopen way
613 	if (fd < 0) {
614 		fprintf(stderr, "Problem detected while creating the input file, %s, aborting!\n", strerror(errno));
615 		exit(-1);
616 	}
617 	write(fd, data, size);
618 	close(fd);
619 	process_database(name);
620 	remove(name);
621 
622 	return 0;
623 }
624 #endif
625