Mercurial > projects > sencrypt
comparison sencrypt.c @ 0:73af139d1a94
Initial revision
author | Guido Berhoerster <guido+sencrypt@berhoerster.name> |
---|---|
date | Tue, 28 Jan 2014 19:23:16 +0100 |
parents | |
children | f230c550e261 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:73af139d1a94 |
---|---|
1 /* | |
2 * Copyright (C) 2011 Guido Berhoerster <guido+sencrypt@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 #define _XOPEN_SOURCE 600 | |
25 | |
26 #include <stdio.h> | |
27 #include <string.h> | |
28 #include <stdint.h> | |
29 #include <stdbool.h> | |
30 #include <unistd.h> | |
31 #include <limits.h> | |
32 #include <libgen.h> | |
33 #include <arpa/inet.h> | |
34 #include <sys/stat.h> | |
35 #include <openssl/conf.h> | |
36 #include <openssl/rand.h> | |
37 #include <openssl/evp.h> | |
38 #include <openssl/err.h> | |
39 | |
40 #ifdef HAVE_ERR_H | |
41 #include <err.h> | |
42 #else | |
43 #include "err.h" | |
44 #endif /* HAVE_ERR_H */ | |
45 | |
46 #define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |
47 | |
48 #define EXIT_USAGE 2 | |
49 | |
50 #define SENCRYPT_FORMAT_VERSION 1 | |
51 #define PBKDF2_ITERATIONS 1000 | |
52 #define SALT_LEN 16 | |
53 #define BUFFER_SIZE (16 * 1024) | |
54 #define MAX_PASSWORD_LEN 256 | |
55 | |
56 enum { | |
57 CMD_SENCRYPT, | |
58 CMD_SDECRYPT | |
59 }; | |
60 | |
61 static void | |
62 openssl_warn(void) { | |
63 unsigned long errcode; | |
64 | |
65 while ((errcode = ERR_get_error()) != 0) { | |
66 warnx("%s", ERR_error_string(errcode, NULL)); | |
67 } | |
68 } | |
69 | |
70 static size_t | |
71 read_keyfile(const char *filename, unsigned char *key, size_t key_size_max) | |
72 { | |
73 size_t keyfile_size = 0; | |
74 FILE *fp = NULL; | |
75 struct stat statbuf; | |
76 | |
77 fp = fopen(filename, "r"); | |
78 if (fp == NULL) { | |
79 warn("could not open key file \"%s\"", filename); | |
80 goto out; | |
81 } | |
82 | |
83 if (fstat(fileno(fp), &statbuf) == -1) { | |
84 warn("could not stat key file \"%s\"", filename); | |
85 goto out; | |
86 } | |
87 | |
88 if (!S_ISREG(statbuf.st_mode)) { | |
89 warnx("key file \"%s\" is not a regular file", filename); | |
90 goto out; | |
91 } | |
92 | |
93 if ((uintmax_t)statbuf.st_size > SIZE_MAX) { | |
94 warnx("key file \"%s\" is too large", filename); | |
95 goto out; | |
96 } | |
97 keyfile_size = (size_t)statbuf.st_size; | |
98 if ((keyfile_size > key_size_max) || | |
99 (keyfile_size == 0)) { | |
100 warnx("invalid key size"); | |
101 goto out; | |
102 } | |
103 | |
104 if (fread(key, 1, keyfile_size, fp) != keyfile_size) { | |
105 warnx("could not read key file \"%s\"", filename); | |
106 goto out; | |
107 } | |
108 | |
109 out: | |
110 if (fp != NULL) { | |
111 fclose(fp); | |
112 } | |
113 | |
114 return (keyfile_size); | |
115 } | |
116 | |
117 static int | |
118 find_algorithm(const char *algo_name, const EVP_CIPHER **cipher_ptr, | |
119 size_t *key_len_ptr) | |
120 { | |
121 int retval = 0; | |
122 const EVP_CIPHER *cipher = NULL; | |
123 size_t key_len = *key_len_ptr; | |
124 | |
125 if (strcmp(algo_name, "aes") == 0) { | |
126 switch (key_len) { | |
127 case 0: | |
128 key_len = 16; | |
129 case 16: | |
130 cipher = EVP_aes_128_cbc(); | |
131 break; | |
132 case 24: | |
133 cipher = EVP_aes_192_cbc(); | |
134 break; | |
135 case 32: | |
136 cipher = EVP_aes_256_cbc(); | |
137 break; | |
138 default: | |
139 warnx("invalid key length %zu", key_len); | |
140 retval = -1; | |
141 } | |
142 } else if (strcmp(algo_name, "arcfour") == 0) { | |
143 if (key_len == 0) { | |
144 key_len = 16; | |
145 cipher = EVP_rc4(); | |
146 } else if (key_len <= EVP_MAX_KEY_LENGTH) { | |
147 /* | |
148 * for RC4 keys are not used verbatim but dervied using | |
149 * PBKDF2 with a hardcoded key length of 128 bit | |
150 */ | |
151 key_len = 16; | |
152 cipher = EVP_rc4(); | |
153 } else { | |
154 warnx("invalid key length %zu", key_len); | |
155 retval = -1; | |
156 } | |
157 } else if (strcmp(algo_name, "des") == 0) { | |
158 if (key_len == 0) { | |
159 key_len = 8; | |
160 cipher = EVP_des_cbc(); | |
161 } else if (key_len == 8) { | |
162 cipher = EVP_des_cbc(); | |
163 } else { | |
164 warnx("invalid key length %zu", key_len); | |
165 retval = -1; | |
166 } | |
167 } else if (strcmp(algo_name, "3des") == 0) { | |
168 if (key_len == 0) { | |
169 key_len = 24; | |
170 cipher = EVP_des_ede3_cbc(); | |
171 } else if (key_len == 24) { | |
172 cipher = EVP_des_ede3_cbc(); | |
173 } else { | |
174 warnx("invalid key length %zu", key_len); | |
175 retval = -1; | |
176 } | |
177 } else { | |
178 warnx("unknown algorithm \"%s\"", algo_name); | |
179 retval = -1; | |
180 } | |
181 | |
182 *cipher_ptr = cipher; | |
183 *key_len_ptr = key_len; | |
184 | |
185 return (retval); | |
186 } | |
187 | |
188 static int | |
189 read_header(BIO *bio_in, uint32_t *iterations, unsigned char *iv, int iv_len, | |
190 unsigned char *salt, int salt_len) | |
191 { | |
192 int read_len; | |
193 uint32_t version; | |
194 int retval = 0; | |
195 | |
196 read_len = BIO_read(bio_in, &version, sizeof (version)); | |
197 if (read_len != sizeof (version)) { | |
198 warnx("failed to read version from input file"); | |
199 if (read_len < 0) { | |
200 openssl_warn(); | |
201 } | |
202 retval = -1; | |
203 goto out; | |
204 } | |
205 version = htonl(version); | |
206 if (version != SENCRYPT_FORMAT_VERSION) { | |
207 warnx("unknown format version %d", version); | |
208 retval = -1; | |
209 goto out; | |
210 } | |
211 | |
212 read_len = BIO_read(bio_in, iterations, sizeof (*iterations)); | |
213 if (read_len != sizeof (*iterations)) { | |
214 warnx("failed to read iterations from input file"); | |
215 if (read_len < 0) { | |
216 openssl_warn(); | |
217 } | |
218 retval = -1; | |
219 goto out; | |
220 } | |
221 *iterations = htonl(*iterations); | |
222 if ((*iterations == 0) || ((sizeof (int) <= sizeof (uint32_t)) && | |
223 (*iterations > INT_MAX))) { | |
224 warnx("invalid number of iterations"); | |
225 retval = -1; | |
226 goto out; | |
227 } | |
228 | |
229 if (iv_len > 0) { | |
230 read_len = BIO_read(bio_in, iv, iv_len); | |
231 if (read_len != iv_len) { | |
232 warnx("failed to read IV from input file"); | |
233 if (read_len < 0) { | |
234 openssl_warn(); | |
235 } | |
236 retval = -1; | |
237 goto out; | |
238 } | |
239 } | |
240 | |
241 read_len = BIO_read(bio_in, salt, salt_len); | |
242 if (read_len != salt_len) { | |
243 warnx("failed to read salt from input file"); | |
244 if (read_len < 0) { | |
245 openssl_warn(); | |
246 } | |
247 retval = -1; | |
248 goto out; | |
249 } | |
250 | |
251 out: | |
252 return (retval); | |
253 } | |
254 | |
255 static int | |
256 sencrypt(const EVP_CIPHER *cipher, BIO *bio_in, BIO *bio_out, | |
257 const unsigned char *key, size_t key_len, const unsigned char *iv, | |
258 const unsigned char *salt) | |
259 { | |
260 int retval = 0; | |
261 uint32_t version; | |
262 uint32_t iterations; | |
263 int iv_len; | |
264 int write_len; | |
265 int read_len; | |
266 BIO *bio_cipher = NULL; | |
267 char *buf = NULL; | |
268 EVP_CIPHER_CTX *cipher_ctx; | |
269 | |
270 /* set up cipher filter */ | |
271 bio_cipher = BIO_new(BIO_f_cipher()); | |
272 BIO_set_cipher(bio_cipher, cipher, NULL, NULL, 1); | |
273 BIO_get_cipher_ctx(bio_cipher, &cipher_ctx); | |
274 if (EVP_CIPHER_CTX_set_key_length(cipher_ctx, (int)key_len) != 1) { | |
275 warnx("failed to set key length"); | |
276 openssl_warn(); | |
277 retval = 1; | |
278 goto out; | |
279 } | |
280 if (EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, iv, 1) != 1) { | |
281 warnx("failed to initialize cipher"); | |
282 openssl_warn(); | |
283 retval = 1; | |
284 goto out; | |
285 } | |
286 BIO_push(bio_cipher, bio_out); | |
287 | |
288 /* write header */ | |
289 version = htonl(SENCRYPT_FORMAT_VERSION); | |
290 write_len = BIO_write(bio_out, &version, sizeof (version)); | |
291 if (write_len != sizeof (version)) { | |
292 warnx("failed to write version to output file"); | |
293 if (write_len < 0) { | |
294 openssl_warn(); | |
295 } | |
296 retval = 1; | |
297 goto out; | |
298 } | |
299 | |
300 iterations = htonl(PBKDF2_ITERATIONS); | |
301 write_len = BIO_write(bio_out, &iterations, sizeof (iterations)); | |
302 if (write_len != sizeof (iterations)) { | |
303 warnx("failed to write iterations to output file"); | |
304 if (write_len < 0) { | |
305 openssl_warn(); | |
306 } | |
307 retval = 1; | |
308 goto out; | |
309 } | |
310 | |
311 iv_len = EVP_CIPHER_iv_length(cipher); | |
312 if (iv_len > 0) { | |
313 write_len = BIO_write(bio_out, iv, iv_len); | |
314 if (write_len != iv_len) { | |
315 warnx("failed to write IV to output file"); | |
316 if (write_len < 0) { | |
317 openssl_warn(); | |
318 } | |
319 retval = 1; | |
320 goto out; | |
321 } | |
322 } | |
323 | |
324 write_len = BIO_write(bio_out, salt, SALT_LEN); | |
325 if (write_len != SALT_LEN) { | |
326 warnx("failed to write salt to output file"); | |
327 if (write_len < 0) { | |
328 openssl_warn(); | |
329 } | |
330 retval = 1; | |
331 goto out; | |
332 } | |
333 | |
334 if (BIO_flush(bio_out) < 1) { | |
335 warnx("failed to flush output file"); | |
336 openssl_warn(); | |
337 retval = 1; | |
338 goto out; | |
339 } | |
340 | |
341 buf = malloc(BUFFER_SIZE); | |
342 if (buf == NULL) { | |
343 warn(NULL); | |
344 retval = 1; | |
345 goto out; | |
346 } | |
347 | |
348 /* encrypt data */ | |
349 while ((read_len = BIO_read(bio_in, buf, BUFFER_SIZE)) > 0) { | |
350 if ((write_len = BIO_write(bio_cipher, buf, read_len)) != | |
351 read_len) { | |
352 warnx("failed to write to output file"); | |
353 if (write_len < 0) { | |
354 openssl_warn(); | |
355 } | |
356 retval = 1; | |
357 goto out; | |
358 } | |
359 } | |
360 if (read_len < 0) { | |
361 warnx("failed to read from input file"); | |
362 openssl_warn(); | |
363 retval = 1; | |
364 goto out; | |
365 } | |
366 | |
367 if (BIO_flush(bio_cipher) < 1) { | |
368 warnx("failed to flush output file"); | |
369 openssl_warn(); | |
370 retval = 1; | |
371 goto out; | |
372 } | |
373 | |
374 out: | |
375 free(buf); | |
376 | |
377 if (bio_cipher != NULL) { | |
378 BIO_pop(bio_cipher); | |
379 BIO_free(bio_cipher); | |
380 } | |
381 | |
382 return (retval); | |
383 } | |
384 | |
385 static int | |
386 sdecrypt(const EVP_CIPHER *cipher, BIO *bio_in, BIO *bio_out, | |
387 const unsigned char *key, size_t key_len, const unsigned char *iv) | |
388 { | |
389 int read_len; | |
390 BIO *bio_cipher = NULL; | |
391 int write_len; | |
392 char *buf = NULL; | |
393 EVP_CIPHER_CTX *cipher_ctx; | |
394 int retval = 0; | |
395 | |
396 buf = malloc(BUFFER_SIZE); | |
397 if (buf == NULL) { | |
398 warn(NULL); | |
399 retval = 1; | |
400 goto out; | |
401 } | |
402 | |
403 /* set up cipher filter */ | |
404 bio_cipher = BIO_new(BIO_f_cipher()); | |
405 BIO_set_cipher(bio_cipher, cipher, NULL, NULL, 0); | |
406 BIO_get_cipher_ctx(bio_cipher, &cipher_ctx); | |
407 if (EVP_CIPHER_CTX_set_key_length(cipher_ctx, (int)key_len) != 1) { | |
408 warnx("failed to set key length"); | |
409 openssl_warn(); | |
410 retval = 1; | |
411 goto out; | |
412 } | |
413 if (EVP_CipherInit_ex(cipher_ctx, NULL, NULL, key, iv, 0) != 1) { | |
414 warnx("failed to initialize cipher"); | |
415 openssl_warn(); | |
416 retval = 1; | |
417 goto out; | |
418 } | |
419 BIO_push(bio_cipher, bio_in); | |
420 | |
421 /* decrypt data */ | |
422 while ((read_len = BIO_read(bio_cipher, buf, BUFFER_SIZE)) > 0) { | |
423 if ((write_len = BIO_write(bio_out, buf, read_len)) != | |
424 read_len) { | |
425 warnx("failed to write to to output file"); | |
426 if (write_len < 0) { | |
427 openssl_warn(); | |
428 } | |
429 retval = 1; | |
430 goto out; | |
431 } | |
432 } | |
433 if (read_len < 0) { | |
434 warnx("failed to read from input file"); | |
435 openssl_warn(); | |
436 retval = 1; | |
437 goto out; | |
438 } | |
439 | |
440 if (BIO_flush(bio_out) < 1) { | |
441 warnx("failed to flush output file"); | |
442 openssl_warn(); | |
443 retval = 1; | |
444 goto out; | |
445 } | |
446 | |
447 if (BIO_get_cipher_status(bio_cipher) == 0) { | |
448 warnx("decryption failed"); | |
449 openssl_warn(); | |
450 retval = 1; | |
451 goto out; | |
452 } | |
453 | |
454 out: | |
455 free(buf); | |
456 | |
457 if (bio_cipher != NULL) { | |
458 BIO_pop(bio_cipher); | |
459 BIO_free(bio_cipher); | |
460 } | |
461 | |
462 return (retval); | |
463 } | |
464 | |
465 static void | |
466 list_algorithms(void) | |
467 { | |
468 printf("Algorithm Keysize: Min Max (bits)\n" | |
469 "------------------------------------------\n"); | |
470 printf("%-15s %5u %5u\n", "aes", 128, 256); | |
471 printf("%-15s %5u %5u\n", "arcfour", 8, | |
472 EVP_MAX_KEY_LENGTH * 8); | |
473 printf("%-15s %5u %5u\n", "des", 64, 64); | |
474 printf("%-15s %5u %5u\n", "3des", 192, 192); | |
475 } | |
476 | |
477 static void | |
478 usage(int cmd) | |
479 { | |
480 if (cmd == CMD_SENCRYPT) { | |
481 fprintf(stderr, "usage: sencrypt -l | [-v] -a algorithm " | |
482 "[-k key_file] [-i input_file] [-o output_file]\n"); | |
483 } else if (cmd == CMD_SDECRYPT) { | |
484 fprintf(stderr, "usage: sdecrypt -l | [-v] -a algorithm " | |
485 "[-k key_file] [-i input_file] [-o output_file]\n"); | |
486 } | |
487 } | |
488 | |
489 int | |
490 main(int argc, char *argv[]) | |
491 { | |
492 char *progname; | |
493 int cmd; | |
494 int c; | |
495 bool aflag = false; | |
496 char *algo_name = NULL; | |
497 bool is_algo_rc4 = false; | |
498 bool iflag = false; | |
499 char *in_filename = NULL; | |
500 bool kflag = false; | |
501 char *key_filename = NULL; | |
502 bool lflag = false; | |
503 bool oflag = false; | |
504 char *out_filename = NULL; | |
505 bool vflag = false; | |
506 bool errflag = false; | |
507 unsigned char key[EVP_MAX_KEY_LENGTH]; | |
508 size_t key_len = 0; | |
509 size_t key_file_len; | |
510 const EVP_CIPHER *cipher; | |
511 BIO *bio_in = NULL; | |
512 uint32_t iterations = PBKDF2_ITERATIONS; | |
513 unsigned char iv[EVP_MAX_IV_LENGTH]; | |
514 unsigned char salt[SALT_LEN]; | |
515 BIO *bio_out = NULL; | |
516 int need_tmpfile = 0; | |
517 FILE *fp_in; | |
518 struct stat statbuf_in; | |
519 struct stat statbuf_out; | |
520 int fd_tmp = -1; | |
521 FILE *fp_tmp = NULL; | |
522 char *out_filename_tmp = NULL; | |
523 char *out_dir = NULL; | |
524 char *tmp_filename = NULL; | |
525 int len; | |
526 mode_t old_mode; | |
527 char pwdata[MAX(MAX_PASSWORD_LEN, EVP_MAX_KEY_LENGTH)]; | |
528 size_t pwdata_len = 0; | |
529 int status = EXIT_SUCCESS; | |
530 | |
531 /* initialize OpenSSL */ | |
532 OpenSSL_add_all_algorithms(); | |
533 ERR_load_crypto_strings(); | |
534 OPENSSL_config(NULL); | |
535 | |
536 progname = strrchr(argv[0], '/'); | |
537 progname = (progname != NULL) ? progname + 1 : argv[0]; | |
538 if ((strcmp(progname, "sencrypt") == 0) || | |
539 (strcmp(progname, "encrypt") == 0)) { | |
540 cmd = CMD_SENCRYPT; | |
541 } else if ((strcmp(progname, "sdecrypt") == 0) || | |
542 (strcmp(progname, "decrypt") == 0)) { | |
543 cmd = CMD_SDECRYPT; | |
544 } else { | |
545 fprintf(stderr, "invalid command name"); | |
546 status = EXIT_FAILURE; | |
547 goto out; | |
548 } | |
549 | |
550 while (!errflag && (c = getopt(argc, argv, "a:i:k:lo:v")) != -1) { | |
551 switch (c) { | |
552 case 'a': | |
553 aflag = true; | |
554 algo_name = optarg; | |
555 is_algo_rc4 = (strcmp(algo_name, "arcfour") == 0); | |
556 break; | |
557 case 'i': | |
558 iflag = true; | |
559 in_filename = optarg; | |
560 break; | |
561 case 'k': | |
562 kflag = true; | |
563 key_filename = optarg; | |
564 break; | |
565 case 'l': | |
566 lflag = true; | |
567 break; | |
568 case 'o': | |
569 oflag = true; | |
570 out_filename = optarg; | |
571 break; | |
572 case 'v': | |
573 vflag = true; | |
574 break; | |
575 default: | |
576 errflag = true; | |
577 } | |
578 } | |
579 if (errflag || (!lflag && !aflag) || (lflag && aflag) || | |
580 (argc > optind)) { | |
581 usage(cmd); | |
582 status = EXIT_USAGE; | |
583 goto out; | |
584 } | |
585 | |
586 if (lflag) { | |
587 list_algorithms(); | |
588 goto out; | |
589 } | |
590 | |
591 if (kflag) { | |
592 key_file_len = read_keyfile(key_filename, key, | |
593 (off_t)sizeof (key)); | |
594 if (key_file_len < 1) { | |
595 status = EXIT_FAILURE; | |
596 goto out; | |
597 } | |
598 key_len = key_file_len; | |
599 } else { | |
600 if (EVP_read_pw_string(pwdata, sizeof (pwdata), "Enter key:", | |
601 (cmd == CMD_SENCRYPT) ? 1 : 0) != 0) { | |
602 warnx("could not read passphrase"); | |
603 openssl_warn(); | |
604 status = EXIT_FAILURE; | |
605 goto out; | |
606 } | |
607 pwdata_len = strlen(pwdata); | |
608 if (pwdata_len < 1) { | |
609 warnx("invalid passphrase"); | |
610 status = EXIT_FAILURE; | |
611 goto out; | |
612 } | |
613 } | |
614 | |
615 /* the cipher is determined based on name and length of the key file */ | |
616 if (find_algorithm(algo_name, &cipher, &key_len) == -1) { | |
617 status = EXIT_FAILURE; | |
618 goto out; | |
619 } | |
620 | |
621 if (iflag) { | |
622 bio_in = BIO_new_file(in_filename, "r"); | |
623 } else { | |
624 bio_in = BIO_new_fp(stdin, BIO_NOCLOSE); | |
625 } | |
626 if (bio_in == NULL) { | |
627 warnx("could not open input file"); | |
628 openssl_warn(); | |
629 status = EXIT_FAILURE; | |
630 goto out; | |
631 } | |
632 | |
633 if (cmd == CMD_SENCRYPT) { | |
634 /* generate random salt and IV */ | |
635 if ((RAND_bytes(salt, sizeof (salt)) == 0) || | |
636 (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) == 0)) { | |
637 /* not enough entropy or unknown error */ | |
638 warnx("failed to generate random data"); | |
639 status = EXIT_FAILURE; | |
640 goto out; | |
641 } | |
642 } else { | |
643 read_header(bio_in, &iterations, iv, | |
644 EVP_CIPHER_iv_length(cipher), salt, (int)sizeof (salt)); | |
645 } | |
646 | |
647 /* | |
648 * if no keyfile was given or the RC4 cipher is used, derive the key | |
649 * from the password and salt | |
650 */ | |
651 if (kflag && is_algo_rc4) { | |
652 memcpy(pwdata, key, key_file_len); | |
653 pwdata_len = key_file_len; | |
654 } | |
655 if (!kflag || is_algo_rc4) { | |
656 if (PKCS5_PBKDF2_HMAC_SHA1(pwdata, (int)pwdata_len, salt, | |
657 sizeof (salt), (int)iterations, (int)key_len, key) == 0) { | |
658 warnx("failed to generate key"); | |
659 status = EXIT_FAILURE; | |
660 goto out; | |
661 } | |
662 } | |
663 | |
664 if (oflag) { | |
665 /* | |
666 * if input and output files are identical, create and write the | |
667 * output to a temporary file for the output which is then | |
668 * renamed to out_filename | |
669 */ | |
670 if (iflag) { | |
671 BIO_get_fp(bio_in, &fp_in); | |
672 if (fstat(fileno(fp_in), &statbuf_in) == -1) { | |
673 warn("could not stat input file"); | |
674 status = EXIT_FAILURE; | |
675 goto out; | |
676 } | |
677 if (stat(out_filename, &statbuf_out) == -1) { | |
678 if (errno != ENOENT) { | |
679 warn("could not stat output file"); | |
680 status = EXIT_FAILURE; | |
681 goto out; | |
682 } | |
683 } else if ((statbuf_in.st_ino == statbuf_out.st_ino) && | |
684 (statbuf_in.st_dev == statbuf_out.st_dev)) { | |
685 need_tmpfile = 1; | |
686 } | |
687 } | |
688 | |
689 if (need_tmpfile) { | |
690 out_filename_tmp = strdup(out_filename); | |
691 if (out_filename_tmp == NULL) { | |
692 warn(NULL); | |
693 status = EXIT_FAILURE; | |
694 goto out; | |
695 } | |
696 out_dir = dirname(out_filename_tmp); | |
697 len = snprintf(NULL, 0, "%s/sencryptXXXXXX", out_dir); | |
698 if (len < 0) { | |
699 warn(NULL); | |
700 status = EXIT_FAILURE; | |
701 goto out; | |
702 } | |
703 tmp_filename = malloc((size_t)len + 1); | |
704 if (tmp_filename == NULL) { | |
705 warn(NULL); | |
706 status = EXIT_FAILURE; | |
707 goto out; | |
708 } | |
709 if (snprintf(tmp_filename, (size_t)len + 1, | |
710 "%s/sencryptXXXXXX", out_dir) != len) { | |
711 warn(NULL); | |
712 status = EXIT_FAILURE; | |
713 goto out; | |
714 } | |
715 old_mode = umask(077); | |
716 fd_tmp = mkstemp(tmp_filename); | |
717 umask(old_mode); | |
718 if (fd_tmp == -1) { | |
719 warn("could not create temporary file"); | |
720 status = EXIT_FAILURE; | |
721 goto out; | |
722 } | |
723 fp_tmp = fdopen(fd_tmp, "w"); | |
724 if (fp_tmp == NULL) { | |
725 warn("could not open temporary file"); | |
726 status = EXIT_FAILURE; | |
727 goto out; | |
728 } | |
729 fd_tmp = -1; | |
730 bio_out = BIO_new_fp(fp_tmp, BIO_CLOSE); | |
731 if (bio_out == NULL) { | |
732 warnx("could not open temporary file"); | |
733 openssl_warn(); | |
734 status = EXIT_FAILURE; | |
735 goto out; | |
736 } | |
737 fp_tmp = NULL; | |
738 } else { | |
739 old_mode = umask(077); | |
740 bio_out = BIO_new_file(out_filename, "w"); | |
741 umask(old_mode); | |
742 if (bio_out == NULL) { | |
743 warnx("could not open output file"); | |
744 openssl_warn(); | |
745 status = EXIT_FAILURE; | |
746 goto out; | |
747 } | |
748 } | |
749 } else { | |
750 bio_out = BIO_new_fp(stdout, BIO_NOCLOSE); | |
751 if (bio_out == NULL) { | |
752 warnx("could not open output file"); | |
753 openssl_warn(); | |
754 status = EXIT_FAILURE; | |
755 goto out; | |
756 } | |
757 } | |
758 | |
759 if (cmd == CMD_SENCRYPT) { | |
760 if (sencrypt(cipher, bio_in, bio_out, key, key_len, | |
761 iv, salt) == -1) { | |
762 status = EXIT_FAILURE; | |
763 } | |
764 } else { | |
765 if (sdecrypt(cipher, bio_in, bio_out, key, key_len, | |
766 iv) == -1) { | |
767 status = EXIT_FAILURE; | |
768 } | |
769 } | |
770 | |
771 out: | |
772 OPENSSL_cleanse(pwdata, pwdata_len); | |
773 OPENSSL_cleanse(key, key_len); | |
774 | |
775 if (fd_tmp != -1) { | |
776 close(fd_tmp); | |
777 } | |
778 | |
779 if (fp_tmp != NULL) { | |
780 fclose(fp_tmp); | |
781 } | |
782 | |
783 if (bio_in != NULL) { | |
784 BIO_free_all(bio_in); | |
785 } | |
786 | |
787 if (bio_out != NULL) { | |
788 BIO_free_all(bio_out); | |
789 | |
790 if (status == 0) { | |
791 if (need_tmpfile) { | |
792 if (rename(tmp_filename, out_filename) == -1) { | |
793 warn("could not create output file"); | |
794 status = EXIT_FAILURE; | |
795 unlink(tmp_filename); | |
796 } | |
797 } | |
798 } else { | |
799 if (need_tmpfile) { | |
800 unlink(tmp_filename); | |
801 } else if (oflag) { | |
802 unlink(out_filename); | |
803 } | |
804 } | |
805 } | |
806 | |
807 free(out_filename_tmp); | |
808 free(tmp_filename); | |
809 | |
810 EVP_cleanup(); | |
811 ERR_free_strings(); | |
812 CONF_modules_free(); | |
813 | |
814 exit(status); | |
815 } |