mirror of https://github.com/skeeto/enchive.git
Add a w32 compatability layer (WIP)
parent
39bc92455f
commit
ec929a6696
5
Makefile
5
Makefile
|
@ -6,11 +6,12 @@ PREFIX = /usr/local
|
|||
|
||||
sources = src/enchive.c src/chacha.c src/curve25519-donna.c src/sha256.c
|
||||
objects = $(sources:.c=.o)
|
||||
headers = config.h src/docs.h src/chacha.h src/sha256.h src/optparse.h
|
||||
headers = config.h src/docs.h src/chacha.h src/sha256.h src/optparse.h \
|
||||
src/w32-compat.h
|
||||
|
||||
enchive: $(objects)
|
||||
$(CC) $(LDFLAGS) -o $@ $(objects) $(LDLIBS)
|
||||
src/enchive.o: src/enchive.c config.h src/docs.h
|
||||
src/enchive.o: src/enchive.c config.h src/docs.h src/w32-compat.h
|
||||
src/chacha.o: src/chacha.c config.h
|
||||
src/curve25519-donna.o: src/curve25519-donna.c config.h
|
||||
src/sha256.o: src/sha256.c config.h
|
||||
|
|
406
src/enchive.c
406
src/enchive.c
|
@ -5,6 +5,18 @@
|
|||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "w32-compat.h"
|
||||
#else
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include "docs.h"
|
||||
#include "sha256.h"
|
||||
#include "chacha.h"
|
||||
|
@ -24,55 +36,9 @@ static int global_agent_timeout = 0;
|
|||
|
||||
static const char enchive_suffix[] = ".enchive";
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
FILE *file;
|
||||
} cleanup[2];
|
||||
|
||||
/**
|
||||
* Register a file for deletion should fatal() be called.
|
||||
*/
|
||||
static void
|
||||
cleanup_register(FILE *file, char *name)
|
||||
{
|
||||
if (file) {
|
||||
unsigned i;
|
||||
for (i = 0; i < sizeof(cleanup) / sizeof(*cleanup); i++) {
|
||||
if (!cleanup[i].name) {
|
||||
cleanup[i].name = name;
|
||||
cleanup[i].file = file;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cleanup registry to indicate FILE has been closed.
|
||||
*/
|
||||
static void
|
||||
cleanup_closed(FILE *file)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < sizeof(cleanup) / sizeof(*cleanup); i++) {
|
||||
if (file == cleanup[i].file)
|
||||
cleanup[i].file = 0;
|
||||
return;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Free resources held by the cleanup registry.
|
||||
*/
|
||||
static void
|
||||
cleanup_free(void)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < sizeof(cleanup) / sizeof(*cleanup); i++)
|
||||
free(cleanup[i].name);
|
||||
}
|
||||
static const char *cleanup_pubfile;
|
||||
static const char *cleanup_secfile;
|
||||
static const char *cleanup_outfile;
|
||||
|
||||
/**
|
||||
* Print a message, cleanup, and exit the program with a failure code.
|
||||
|
@ -80,18 +46,17 @@ cleanup_free(void)
|
|||
static void
|
||||
fatal(const char *fmt, ...)
|
||||
{
|
||||
unsigned i;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "enchive: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fputc('\n', stderr);
|
||||
for (i = 0; i < sizeof(cleanup) / sizeof(*cleanup); i++) {
|
||||
if (cleanup[i].file)
|
||||
fclose(cleanup[i].file);
|
||||
if (cleanup[i].name)
|
||||
remove(cleanup[i].name);
|
||||
}
|
||||
if (cleanup_pubfile)
|
||||
remove(cleanup_pubfile);
|
||||
if (cleanup_secfile)
|
||||
remove(cleanup_secfile);
|
||||
if (cleanup_outfile)
|
||||
remove(cleanup_outfile);
|
||||
va_end(ap);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
@ -164,6 +129,21 @@ joinstr(int n, ...)
|
|||
return str;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
full_read(int fd, void *buf, size_t len)
|
||||
{
|
||||
size_t z = 0;
|
||||
while (z < len) {
|
||||
ssize_t r = read(fd, (char *)buf + z, len - z);
|
||||
if (r == -1)
|
||||
return -1;
|
||||
if (r == 0)
|
||||
break;
|
||||
z += r;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the protection key from a key agent identified by its IV.
|
||||
*/
|
||||
|
@ -175,11 +155,6 @@ static int agent_read(u8 *key, const u8 *id);
|
|||
static int agent_run(const u8 *key, const u8 *id);
|
||||
|
||||
#if ENCHIVE_OPTION_AGENT
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/**
|
||||
* Fill ADDR with a unix domain socket name for the agent.
|
||||
|
@ -299,6 +274,7 @@ agent_read(u8 *key, const u8 *id)
|
|||
{
|
||||
(void)key;
|
||||
(void)id;
|
||||
(void)warning;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -408,118 +384,46 @@ storage_directory(char *file)
|
|||
/**
|
||||
* Read a passphrase directly from the keyboard without echo.
|
||||
*/
|
||||
static void get_passphrase(char *buf, size_t len, char *prompt);
|
||||
|
||||
/**
|
||||
* Read a passphrase without any fanfare (fallback).
|
||||
*/
|
||||
static void
|
||||
get_passphrase_dumb(char *buf, size_t len, char *prompt)
|
||||
{
|
||||
size_t passlen;
|
||||
warning("reading passphrase from stdin with echo");
|
||||
fputs(prompt, stderr);
|
||||
fflush(stderr);
|
||||
if (!fgets(buf, len, stdin))
|
||||
fatal("could not read passphrase");
|
||||
passlen = strlen(buf);
|
||||
if (buf[passlen - 1] < ' ')
|
||||
buf[passlen - 1] = 0;
|
||||
}
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
|
||||
static void
|
||||
get_passphrase(char *buf, size_t len, char *prompt)
|
||||
{
|
||||
int tty = open("/dev/tty", O_RDWR);
|
||||
if (tty == -1) {
|
||||
get_passphrase_dumb(buf, len, prompt);
|
||||
struct termios old, new;
|
||||
ssize_t z;
|
||||
int tty;
|
||||
|
||||
tty = open("/dev/tty", O_RDWR);
|
||||
if (tty == -1)
|
||||
fatal("could not open /dev/tty -- %s", strerror(errno));
|
||||
|
||||
if (write(tty, prompt, strlen(prompt)) != (ssize_t)strlen(prompt))
|
||||
fatal("could not write to /dev/tty -- %s", strerror(errno));
|
||||
|
||||
if (tcgetattr(tty, &old) == -1)
|
||||
fatal("tcgetattr() -- %s", strerror(errno));
|
||||
new = old;
|
||||
new.c_lflag &= ~ECHO;
|
||||
if (tcsetattr(tty, TCSANOW, &new) == -1)
|
||||
fatal("tcsetattr() -- %s", strerror(errno));
|
||||
|
||||
z = read(tty, buf, len);
|
||||
(void)tcsetattr(tty, TCSANOW, &old); /* don't care if this fails */
|
||||
(void)write(tty, "\n", 1); /* don't care if this fails */
|
||||
|
||||
if (z == -1) {
|
||||
fatal("error readin /dev/tty -- %s", strerror(errno));
|
||||
} else if (z == 0) {
|
||||
buf[z] = 0;
|
||||
} else {
|
||||
char newline = '\n';
|
||||
size_t i = 0;
|
||||
struct termios old, new;
|
||||
if (write(tty, prompt, strlen(prompt)) == -1)
|
||||
fatal("error asking for passphrase");
|
||||
tcgetattr(tty, &old);
|
||||
new = old;
|
||||
new.c_lflag &= ~ECHO;
|
||||
tcsetattr(tty, TCSANOW, &new);
|
||||
errno = 0;
|
||||
while (i < len - 1 && read(tty, buf + i, 1) == 1) {
|
||||
if (buf[i] == '\n' || buf[i] == '\r')
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
buf[i] = 0;
|
||||
tcsetattr(tty, TCSANOW, &old);
|
||||
if (write(tty, &newline, 1) == -1)
|
||||
fatal("error asking for passphrase");
|
||||
close(tty);
|
||||
if (errno)
|
||||
fatal("could not read passphrase from /dev/tty");
|
||||
char *end;
|
||||
buf[z] = 0;
|
||||
if ((end = strchr(buf, '\r')))
|
||||
*end = 0;
|
||||
else if ((end = strchr(buf, '\n')))
|
||||
*end = 0;
|
||||
}
|
||||
close(tty);
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
static void
|
||||
get_passphrase(char *buf, size_t len, char *prompt)
|
||||
{
|
||||
DWORD orig;
|
||||
HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (!GetConsoleMode(in, &orig)) {
|
||||
get_passphrase_dumb(buf, len, prompt);
|
||||
} else {
|
||||
size_t passlen;
|
||||
SetConsoleMode(in, orig & ~ENABLE_ECHO_INPUT);
|
||||
fputs(prompt, stderr);
|
||||
if (!fgets(buf, len, stdin))
|
||||
fatal("could not read passphrase");
|
||||
fputc('\n', stderr);
|
||||
passlen = strlen(buf);
|
||||
if (buf[passlen - 1] < ' ')
|
||||
buf[passlen - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
static void
|
||||
get_passphrase(char *buf, size_t len, char *prompt)
|
||||
{
|
||||
get_passphrase_dumb(buf, len, prompt);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Create/truncate a file with paranoid permissions using OS calls.
|
||||
*/
|
||||
static FILE *secure_creat(const char *file);
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
|
||||
static FILE *
|
||||
secure_creat(const char *file)
|
||||
{
|
||||
int fd = open(file, O_CREAT | O_WRONLY, 00600);
|
||||
if (fd == -1)
|
||||
return 0;
|
||||
return fdopen(fd, "wb");
|
||||
}
|
||||
|
||||
#else
|
||||
static FILE *
|
||||
secure_creat(const char *file)
|
||||
{
|
||||
return fopen(file, "wb");
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize a SHA-256 context for HMAC-SHA256.
|
||||
* All message data will go into the resulting context.
|
||||
|
@ -607,36 +511,17 @@ key_derive(const char *passphrase, u8 *buf, int iexp, const u8 *salt)
|
|||
* Get secure entropy suitable for key generation from OS.
|
||||
* Abort the program if the entropy could not be retrieved.
|
||||
*/
|
||||
static void secure_entropy(void *buf, size_t len);
|
||||
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
static void
|
||||
secure_entropy(void *buf, size_t len)
|
||||
{
|
||||
FILE *r = fopen("/dev/urandom", "rb");
|
||||
if (!r)
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd == -1)
|
||||
fatal("failed to open %s", "/dev/urandom");
|
||||
if (!fread(buf, len, 1, r))
|
||||
if (read(fd, buf, len) != (ssize_t)len)
|
||||
fatal("failed to gather entropy");
|
||||
fclose(r);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
|
||||
static void
|
||||
secure_entropy(void *buf, size_t len)
|
||||
{
|
||||
HCRYPTPROV h = 0;
|
||||
DWORD type = PROV_RSA_FULL;
|
||||
DWORD flags = CRYPT_VERIFYCONTEXT | CRYPT_SILENT;
|
||||
if (!CryptAcquireContext(&h, 0, 0, type, flags) ||
|
||||
!CryptGenRandom(h, len, buf))
|
||||
fatal("failed to gather entropy");
|
||||
CryptReleaseContext(h, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Generate a brand new Curve25519 secret key from system entropy.
|
||||
*/
|
||||
|
@ -672,7 +557,7 @@ compute_shared(u8 *sh, const u8 *s, const u8 *p)
|
|||
* Encrypt from file to file using key/iv, aborting on any error.
|
||||
*/
|
||||
static void
|
||||
symmetric_encrypt(FILE *in, FILE *out, const u8 *key, const u8 *iv)
|
||||
symmetric_encrypt(int in, int out, const u8 *key, const u8 *iv)
|
||||
{
|
||||
static u8 buffer[2][CHACHA_BLOCKLENGTH * 1024];
|
||||
u8 mac[SHA256_BLOCK_SIZE];
|
||||
|
@ -684,34 +569,33 @@ symmetric_encrypt(FILE *in, FILE *out, const u8 *key, const u8 *iv)
|
|||
hmac_init(hmac, key);
|
||||
|
||||
for (;;) {
|
||||
size_t z = fread(buffer[0], 1, sizeof(buffer[0]), in);
|
||||
if (!z) {
|
||||
if (ferror(in))
|
||||
fatal("error reading plaintext file");
|
||||
ssize_t z = full_read(in, buffer[0], sizeof(buffer[0]));
|
||||
if (z < 0)
|
||||
fatal("error reading plaintext file -- %s", strerror(errno));
|
||||
if (z == 0)
|
||||
break;
|
||||
}
|
||||
sha256_update(hmac, buffer[0], z);
|
||||
chacha_encrypt_bytes(ctx, buffer[0], buffer[1], z);
|
||||
if (!fwrite(buffer[1], z, 1, out))
|
||||
fatal("error writing ciphertext file");
|
||||
if (z < sizeof(buffer[0]))
|
||||
if (write(out, buffer[1], z) != z)
|
||||
fatal("error writing ciphertext file -- %s", strerror(errno));
|
||||
if (z < (ssize_t)sizeof(buffer[0]))
|
||||
break;
|
||||
}
|
||||
|
||||
hmac_final(hmac, key, mac);
|
||||
|
||||
if (!fwrite(mac, sizeof(mac), 1, out))
|
||||
fatal("error writing checksum to ciphertext file");
|
||||
if (fflush(out))
|
||||
fatal("error flushing to ciphertext file -- %s", strerror(errno));
|
||||
if (write(out, mac, sizeof(mac)) != sizeof(mac))
|
||||
fatal("error writing checksum to ciphertext file -- %s",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt from file to file using key/iv, aborting on any error.
|
||||
*/
|
||||
static void
|
||||
symmetric_decrypt(FILE *in, FILE *out, const u8 *key, const u8 *iv)
|
||||
symmetric_decrypt(int in, int out, const u8 *key, const u8 *iv)
|
||||
{
|
||||
ssize_t r;
|
||||
static u8 buffer[2][CHACHA_BLOCKLENGTH * 1024 + SHA256_BLOCK_SIZE];
|
||||
u8 mac[SHA256_BLOCK_SIZE];
|
||||
SHA256_CTX hmac[1];
|
||||
|
@ -722,39 +606,34 @@ symmetric_decrypt(FILE *in, FILE *out, const u8 *key, const u8 *iv)
|
|||
hmac_init(hmac, key);
|
||||
|
||||
/* Always keep SHA256_BLOCK_SIZE bytes in the buffer. */
|
||||
if (!(fread(buffer[0], SHA256_BLOCK_SIZE, 1, in))) {
|
||||
if (ferror(in))
|
||||
fatal("cannot read ciphertext file");
|
||||
else
|
||||
fatal("ciphertext file too short");
|
||||
}
|
||||
r = full_read(in, buffer[0], SHA256_BLOCK_SIZE);
|
||||
if (r < 0)
|
||||
fatal("cannot read ciphertext file -- %s", strerror(errno));
|
||||
if (r != SHA256_BLOCK_SIZE)
|
||||
fatal("ciphertext file too short");
|
||||
|
||||
for (;;) {
|
||||
u8 *p = buffer[0] + SHA256_BLOCK_SIZE;
|
||||
size_t z = fread(p, 1, sizeof(buffer[0]) - SHA256_BLOCK_SIZE, in);
|
||||
if (!z) {
|
||||
if (ferror(in))
|
||||
fatal("error reading ciphertext file");
|
||||
ssize_t z = full_read(in, p, sizeof(buffer[0]) - SHA256_BLOCK_SIZE);
|
||||
if (z < 0)
|
||||
fatal("error reading ciphertext file");
|
||||
else if (z == 0)
|
||||
break;
|
||||
}
|
||||
chacha_encrypt_bytes(ctx, buffer[0], buffer[1], z);
|
||||
sha256_update(hmac, buffer[1], z);
|
||||
if (!fwrite(buffer[1], z, 1, out))
|
||||
fatal("error writing plaintext file");
|
||||
if (write(out, buffer[1], z) != z)
|
||||
fatal("error writing plaintext file -- %s", strerror(errno));
|
||||
|
||||
/* Move last SHA256_BLOCK_SIZE bytes to the front. */
|
||||
memmove(buffer[0], buffer[0] + z, SHA256_BLOCK_SIZE);
|
||||
|
||||
if (z < sizeof(buffer[0]) - SHA256_BLOCK_SIZE)
|
||||
if (z < (ssize_t)sizeof(buffer[0]) - SHA256_BLOCK_SIZE)
|
||||
break;
|
||||
}
|
||||
|
||||
hmac_final(hmac, key, mac);
|
||||
if (memcmp(buffer[0], mac, sizeof(mac)) != 0)
|
||||
fatal("checksum mismatch!");
|
||||
if (fflush(out))
|
||||
fatal("error flushing to plaintext file -- %s", strerror(errno));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -781,16 +660,14 @@ default_secfile(void)
|
|||
static void
|
||||
write_pubkey(char *file, u8 *key)
|
||||
{
|
||||
FILE *f = fopen(file, "wb");
|
||||
if (!f)
|
||||
int fd = open(file, O_CREAT | O_WRONLY, 0600);
|
||||
if (fd == -1)
|
||||
fatal("failed to open key file for writing '%s' -- %s",
|
||||
file, strerror(errno));
|
||||
cleanup_register(f, file);
|
||||
if (!fwrite(key, 32, 1, f))
|
||||
cleanup_pubfile = file;
|
||||
if (write(fd, key, 32) != 32)
|
||||
fatal("failed to write key file '%s'", file);
|
||||
cleanup_closed(f);
|
||||
if (fclose(f))
|
||||
fatal("failed to flush key file '%s' -- %s", file, strerror(errno));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* Layout of secret key file */
|
||||
|
@ -806,7 +683,7 @@ write_pubkey(char *file, u8 *key)
|
|||
static void
|
||||
write_seckey(char *file, const u8 *seckey, int iexp)
|
||||
{
|
||||
FILE *secfile;
|
||||
int secfd;
|
||||
chacha_ctx cha[1];
|
||||
SHA256_CTX sha[1];
|
||||
u8 buf[8 + 1 + 3 + 20 + 32] = {0}; /* entire file contents */
|
||||
|
@ -856,15 +733,13 @@ write_seckey(char *file, const u8 *seckey, int iexp)
|
|||
memcpy(buf_seckey, seckey, 32);
|
||||
}
|
||||
|
||||
secfile = secure_creat(file);
|
||||
if (!secfile)
|
||||
secfd = open(file, O_CREAT | O_WRONLY, 0600);
|
||||
if (secfd == -1)
|
||||
fatal("failed to open key file for writing '%s'", file);
|
||||
cleanup_register(secfile, file);
|
||||
if (!fwrite(buf, sizeof(buf), 1, secfile))
|
||||
cleanup_secfile = file;
|
||||
if (write(secfd, buf, sizeof(buf)) != sizeof(buf))
|
||||
fatal("failed to write key file '%s'", file);
|
||||
cleanup_closed(secfile);
|
||||
if (fclose(secfile))
|
||||
fatal("failed to flush key file '%s' -- %s", file, strerror(errno));
|
||||
close(secfd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1198,8 +1073,8 @@ command_archive(struct optparse *options)
|
|||
/* Options */
|
||||
char *infile;
|
||||
char *outfile;
|
||||
FILE *in = stdin;
|
||||
FILE *out = stdout;
|
||||
int in = STDIN_FILENO;
|
||||
int out = STDOUT_FILENO;
|
||||
char *pubfile = dupstr(global_pubkey);
|
||||
int delete = 0;
|
||||
|
||||
|
@ -1229,8 +1104,8 @@ command_archive(struct optparse *options)
|
|||
|
||||
infile = optparse_arg(options);
|
||||
if (infile) {
|
||||
in = fopen(infile, "rb");
|
||||
if (!in)
|
||||
in = open(infile, O_RDONLY);
|
||||
if (in == -1)
|
||||
fatal("could not open input file '%s' -- %s",
|
||||
infile, strerror(errno));
|
||||
}
|
||||
|
@ -1241,11 +1116,11 @@ command_archive(struct optparse *options)
|
|||
outfile = joinstr(2, infile, enchive_suffix);
|
||||
}
|
||||
if (outfile) {
|
||||
out = fopen(outfile, "wb");
|
||||
if (!out)
|
||||
out = open(outfile, O_CREAT | O_WRONLY, 0600);
|
||||
if (out == -1)
|
||||
fatal("could not open output file '%s' -- %s",
|
||||
outfile, strerror(errno));
|
||||
cleanup_register(out, outfile);
|
||||
cleanup_outfile = outfile;
|
||||
}
|
||||
|
||||
/* Generare ephemeral keypair. */
|
||||
|
@ -1258,22 +1133,16 @@ command_archive(struct optparse *options)
|
|||
sha256_update(sha, shared, sizeof(shared));
|
||||
sha256_final(sha, iv);
|
||||
iv[0] += (unsigned)ENCHIVE_FORMAT_VERSION;
|
||||
if (!fwrite(iv, 8, 1, out))
|
||||
if (write(out, iv, 8) != 8)
|
||||
fatal("failed to write IV to archive");
|
||||
if (!fwrite(epublic, sizeof(epublic), 1, out))
|
||||
if (write(out, epublic, sizeof(epublic)) != sizeof(epublic))
|
||||
fatal("failed to write ephemeral key to archive");
|
||||
symmetric_encrypt(in, out, shared, iv);
|
||||
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
if (out != stdout) {
|
||||
cleanup_closed(out);
|
||||
fclose(out); /* already flushed */
|
||||
}
|
||||
|
||||
close(in);
|
||||
close(out);
|
||||
if (delete && infile)
|
||||
remove(infile);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1287,8 +1156,8 @@ command_extract(struct optparse *options)
|
|||
/* Options */
|
||||
char *infile;
|
||||
char *outfile;
|
||||
FILE *in = stdin;
|
||||
FILE *out = stdout;
|
||||
int in = STDIN_FILENO;
|
||||
int out = STDOUT_FILENO;
|
||||
char *secfile = dupstr(global_seckey);
|
||||
int delete = 0;
|
||||
|
||||
|
@ -1318,8 +1187,8 @@ command_extract(struct optparse *options)
|
|||
|
||||
infile = optparse_arg(options);
|
||||
if (infile) {
|
||||
in = fopen(infile, "rb");
|
||||
if (!in)
|
||||
in = open(infile, O_RDONLY);
|
||||
if (in == -1)
|
||||
fatal("could not open input file '%s' -- %s",
|
||||
infile, strerror(errno));
|
||||
}
|
||||
|
@ -1335,17 +1204,18 @@ command_extract(struct optparse *options)
|
|||
outfile[len - slen] = 0;
|
||||
}
|
||||
if (outfile) {
|
||||
out = fopen(outfile, "wb");
|
||||
if (!out)
|
||||
out = open(outfile, O_CREAT | O_WRONLY, 0600);
|
||||
if (out == -1)
|
||||
fatal("could not open output file '%s' -- %s",
|
||||
infile, strerror(errno));
|
||||
cleanup_register(out, outfile);
|
||||
cleanup_outfile = outfile;
|
||||
}
|
||||
|
||||
if (!(fread(iv, sizeof(iv), 1, in)))
|
||||
fatal("failed to read IV from archive");
|
||||
if (!(fread(epublic, sizeof(epublic), 1, in)))
|
||||
fatal("failed to read ephemeral key from archive");
|
||||
if (full_read(in, iv, sizeof(iv)) != sizeof(iv))
|
||||
fatal("failed to read IV from archive -- %s", strerror(errno));
|
||||
if (full_read(in, epublic, sizeof(epublic)) != sizeof(epublic))
|
||||
fatal("failed to read ephemeral key from archive -- %s",
|
||||
strerror(errno));
|
||||
compute_shared(shared, secret, epublic);
|
||||
|
||||
/* Validate key before processing the file. */
|
||||
|
@ -1358,13 +1228,8 @@ command_extract(struct optparse *options)
|
|||
|
||||
symmetric_decrypt(in, out, shared, iv);
|
||||
|
||||
if (in != stdin)
|
||||
fclose(in);
|
||||
if (out != stdout) {
|
||||
cleanup_closed(out);
|
||||
fclose(out); /* already flushed */
|
||||
}
|
||||
|
||||
close(in);
|
||||
close(out);
|
||||
if (delete && infile)
|
||||
remove(infile);
|
||||
}
|
||||
|
@ -1481,6 +1346,5 @@ main(int argc, char **argv)
|
|||
break;
|
||||
}
|
||||
|
||||
cleanup_free();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
#if !defined(W32_COMPAT_H) && defined(_WIN32)
|
||||
#define W32_COMPAT_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma comment(lib, "advapi32.lib")
|
||||
#endif
|
||||
|
||||
typedef SSIZE_T ssize_t;
|
||||
typedef unsigned mode_t;
|
||||
|
||||
#define O_CREAT (1u << 0)
|
||||
#define O_RDONLY (1u << 1)
|
||||
#define O_WRONLY (1u << 2)
|
||||
#define O_RDWR (1u << 3)
|
||||
|
||||
#define ECHO 1
|
||||
#define TCSANOW 0
|
||||
struct termios {
|
||||
int c_lflag;
|
||||
};
|
||||
|
||||
enum w32_fd {
|
||||
FD_NONE,
|
||||
FD_STDIN,
|
||||
FD_STDOUT,
|
||||
FD_STDERR,
|
||||
FD_URANDOM,
|
||||
FD_TTY,
|
||||
FD_FILE
|
||||
};
|
||||
|
||||
static struct {
|
||||
enum w32_fd type;
|
||||
union {
|
||||
HANDLE file;
|
||||
HCRYPTPROV urandom;
|
||||
struct {
|
||||
HANDLE in;
|
||||
HANDLE out;
|
||||
} con;
|
||||
} d;
|
||||
} w32_fds[8] = {
|
||||
{FD_STDIN, {INVALID_HANDLE_VALUE}},
|
||||
{FD_STDOUT, {INVALID_HANDLE_VALUE}},
|
||||
{FD_STDERR, {INVALID_HANDLE_VALUE}}
|
||||
};
|
||||
|
||||
static int
|
||||
open(const char *pathname, int flags, ...)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; fd < 0 && i < sizeof(w32_fds) / sizeof(*w32_fds); i++)
|
||||
if (w32_fds[i].type == FD_NONE)
|
||||
fd = i;
|
||||
if (fd == -1) {
|
||||
errno = EMFILE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(pathname, "/dev/urandom") == 0) {
|
||||
DWORD type = PROV_RSA_FULL;
|
||||
DWORD flag = CRYPT_VERIFYCONTEXT | CRYPT_SILENT;
|
||||
HCRYPTPROV *h = &w32_fds[fd].d.urandom;
|
||||
if (!CryptAcquireContext(h, 0, 0, type, flag)) {
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
assert(flags == O_RDONLY);
|
||||
w32_fds[fd].type = FD_URANDOM;
|
||||
return fd;
|
||||
} else if (strcmp(pathname, "/dev/tty") == 0) {
|
||||
DWORD access = GENERIC_READ | GENERIC_WRITE;
|
||||
DWORD disp = OPEN_EXISTING;
|
||||
DWORD flag = FILE_ATTRIBUTE_NORMAL;
|
||||
HANDLE in = CreateFile("CONIN$", access, 0, 0, disp, flag, 0);
|
||||
HANDLE out = CreateFile("CONOUT$", access, 0, 0, disp, flag, 0);
|
||||
if (in == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(out);
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
if (out == INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(in);
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
assert(flags == O_RDWR);
|
||||
w32_fds[fd].d.con.in = in;
|
||||
w32_fds[fd].d.con.out = out;
|
||||
w32_fds[fd].type = FD_TTY;
|
||||
return fd;
|
||||
} else {
|
||||
HANDLE h;
|
||||
DWORD access;
|
||||
DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||
DWORD disp;
|
||||
DWORD flag = FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
if (flags & O_CREAT)
|
||||
disp = CREATE_ALWAYS;
|
||||
else
|
||||
disp = OPEN_EXISTING;
|
||||
|
||||
if (flags & O_RDWR)
|
||||
access = GENERIC_READ | GENERIC_WRITE;
|
||||
else if (flags & O_RDONLY)
|
||||
access = GENERIC_READ;
|
||||
else if (flags & O_WRONLY)
|
||||
access = GENERIC_WRITE;
|
||||
else
|
||||
abort();
|
||||
|
||||
h = CreateFile(pathname, access, share, 0, disp, flag, 0);
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
errno = EACCES;
|
||||
return -1;
|
||||
}
|
||||
|
||||
w32_fds[fd].d.file = h;
|
||||
w32_fds[fd].type = FD_FILE;
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
close(int fd)
|
||||
{
|
||||
switch (w32_fds[fd].type) {
|
||||
case FD_NONE: {
|
||||
abort();
|
||||
} break;
|
||||
case FD_STDIN:
|
||||
case FD_STDOUT:
|
||||
case FD_STDERR: {
|
||||
abort(); /* unimplemented */
|
||||
} break;
|
||||
case FD_URANDOM: {
|
||||
CryptReleaseContext(w32_fds[fd].d.urandom, 0);
|
||||
} break;
|
||||
case FD_TTY: {
|
||||
CloseHandle(w32_fds[fd].d.con.in);
|
||||
CloseHandle(w32_fds[fd].d.con.out);
|
||||
} break;
|
||||
case FD_FILE: {
|
||||
CloseHandle(w32_fds[fd].d.file);
|
||||
} break;
|
||||
}
|
||||
w32_fds[fd].type = FD_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
read(int fd, void *buf, size_t len)
|
||||
{
|
||||
switch (w32_fds[fd].type) {
|
||||
case FD_NONE: {
|
||||
abort();
|
||||
}
|
||||
case FD_STDIN: {
|
||||
if (w32_fds[fd].d.file == INVALID_HANDLE_VALUE) {
|
||||
DWORD mode;
|
||||
w32_fds[fd].d.file = GetStdHandle(STD_INPUT_HANDLE);
|
||||
assert(!GetConsoleMode(w32_fds[fd].d.file, &mode));
|
||||
}
|
||||
} /* FALLTHROUGH */
|
||||
case FD_FILE: {
|
||||
DWORD actual;
|
||||
HANDLE in = w32_fds[fd].d.file;
|
||||
if (!ReadFile(in, buf, len, &actual, 0)) {
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_BROKEN_PIPE)
|
||||
return 0; /* actually an EOF */
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return actual;
|
||||
} break;
|
||||
case FD_STDOUT:
|
||||
case FD_STDERR: {
|
||||
abort();
|
||||
} break;
|
||||
case FD_URANDOM: {
|
||||
if (!CryptGenRandom(w32_fds[fd].d.urandom, len, buf)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
case FD_TTY: {
|
||||
DWORD actual;
|
||||
BOOL r = ReadConsole(w32_fds[fd].d.con.in, buf, len, &actual, 0);
|
||||
if (!r) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return actual;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
write(int fd, const void *buf, size_t len)
|
||||
{
|
||||
switch (w32_fds[fd].type) {
|
||||
case FD_URANDOM:
|
||||
case FD_NONE: {
|
||||
abort();
|
||||
}
|
||||
case FD_STDIN: {
|
||||
abort();
|
||||
} break;
|
||||
case FD_STDOUT:
|
||||
case FD_STDERR: {
|
||||
if (w32_fds[fd].d.file == INVALID_HANDLE_VALUE) {
|
||||
DWORD mode;
|
||||
if (w32_fds[fd].type == FD_STDOUT)
|
||||
w32_fds[fd].d.file = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
else
|
||||
w32_fds[fd].d.file = GetStdHandle(STD_ERROR_HANDLE);
|
||||
assert(!GetConsoleMode(w32_fds[fd].d.file, &mode));
|
||||
}
|
||||
} /* FALLTHROUGH */
|
||||
case FD_FILE: {
|
||||
DWORD actual;
|
||||
if (!WriteFile(w32_fds[fd].d.file, buf, len, &actual, 0)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return actual;
|
||||
} break;
|
||||
case FD_TTY: {
|
||||
DWORD actual;
|
||||
BOOL r = WriteConsole(w32_fds[fd].d.con.out, buf, len, &actual, 0);
|
||||
if (!r) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
return actual;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static int
|
||||
tcgetattr(int fd, struct termios *s)
|
||||
{
|
||||
assert(w32_fds[fd].type == FD_TTY);
|
||||
s->c_lflag = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tcsetattr(int fd, int actions, struct termios *s)
|
||||
{
|
||||
DWORD orig;
|
||||
HANDLE in = w32_fds[fd].d.con.in;
|
||||
|
||||
assert(w32_fds[fd].type == FD_TTY);
|
||||
assert(actions == TCSANOW);
|
||||
if (GetConsoleMode(in, &orig)) {
|
||||
if (s->c_lflag)
|
||||
SetConsoleMode(in, orig | ENABLE_ECHO_INPUT);
|
||||
else
|
||||
SetConsoleMode(in, orig & ~ENABLE_ECHO_INPUT);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int
|
||||
mkdir(const char *pathname, mode_t mode)
|
||||
{
|
||||
(void)mode;
|
||||
if (CreateDirectory(pathname, 0))
|
||||
return 0;
|
||||
switch (GetLastError()) {
|
||||
case ERROR_ALREADY_EXISTS: {
|
||||
errno = EEXIST;
|
||||
} break;
|
||||
case ERROR_PATH_NOT_FOUND: {
|
||||
errno = ENOENT;
|
||||
} break;
|
||||
default: {
|
||||
errno = EFAULT;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue