Tools to make secret sharing easier.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

376 lines
9.5 KiB

/*
* gfsec - Secret sharing tools
* Copyright (C) 2016 Damien Goutte-Gattat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <getopt.h>
#include <err.h>
#include <libgfshare.h>
#include <gcrypt.h>
#include "util.h"
#include "share.h"
#include "secretcfg.h"
#include "mtp-support.h"
#include "uuid-support.h"
static void
usage(int status)
{
puts("Usage: gfsec-use [options] [command]\n\
Rebuild a shared secret and run the specified command.\n\
If no command is specified, a shell is executed. The rebuilt\n\
secret is destroyed when the command or the shell terminates.\n");
puts("Options:\n\
-h, --help Display this help message.\n\
-v, --version Display the version message.\n");
puts("\
-c, --config FILE Read configuration from the specified\n\
file.\n\
-o, --output FILE Write reconstructed secret in the specified\n\
file (override configuration file).\n");
printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
exit(status);
}
static void
info(void)
{
printf("\
gfsec-use (%s %s)\n\
Copyright (C) 2016 Damien Goutte-Gattat\n\
\n\
This program is released under the GNU General Public License.\n\
See the COPYING file or <http://www.gnu.org/licenses/gpl.html>.\n\
", PACKAGE_NAME, VERSION);
exit(EXIT_SUCCESS);
}
static int
get_config_file_in_dir(const char *filename, char *buffer, size_t len)
{
int rc;
const char *env_info;
if ( (env_info = getenv("XDG_CONFIG_HOME")) )
rc = snprintf(buffer, len, "%s/gfsecret/%s.conf", env_info, filename);
else if ( (env_info = getenv("HOME")) )
rc = snprintf(buffer, len, "%s/.config/gfsecret/%s.conf", env_info, filename);
else {
rc = -1;
errno = ENOENT;
}
if ( rc >= 0 && (unsigned)rc >= len ) {
rc = -1;
errno = ENAMETOOLONG;
}
else
rc = 0;
return rc;
}
static int
get_config_file(const char *filename, char *buffer, size_t len)
{
int rc;
if ( ! filename )
rc = get_config_file_in_dir("default", buffer, len);
else if ( file_exists(filename) == -1 )
rc = get_config_file_in_dir(filename, buffer, len);
else if ( strlen(filename) < len ) {
rc = 0;
strcpy(buffer, filename);
}
else {
rc = -1;
errno = ENAMETOOLONG;
}
return rc;
}
static char *
get_share_display_name(gfsec_share_t *share)
{
static char buffer[256];
char *schemes[] = { "file://", "uuid://", "label://", "mtp://" };
snprintf(buffer, sizeof(buffer), "%s%s%s",
schemes[share->scheme],
share->authority ? share->authority : "",
share->path);
return buffer;
}
static int
get_share_data(gfsec_share_t *share, gcry_md_hd_t md)
{
int rc = -1;
switch ( share->scheme ) {
case SCHEME_FILE:
if ( (share->data = read_file(share->path, &(share->length))) )
rc = 0;
break;
case SCHEME_UUID:
if ( (share->data = gfsec_get_file_from_uuid(share->authority,
share->path, &(share->length))) )
rc = 0;
break;
case SCHEME_LABEL:
if ( (share->data = gfsec_get_file_from_label(share->authority,
share->path, &(share->length))) )
rc = 0;
break;
case SCHEME_MTP:
if ( (share->data = gfsec_get_file_from_mtp(share->authority,
share->path, &(share->length))) )
rc = 0;
break;
}
if ( rc == 0 && share->sha256 ) {
unsigned char *md_val;
gcry_md_write(md, share->data, share->length);
md_val = gcry_md_read(md, 0);
if ( memcmp(share->sha256, md_val, 32) != 0 ) {
warnx("Incorrect hash value for share %s\n", get_share_display_name(share));
free(share->data);
share->data = NULL;
rc = -1;
}
gcry_md_reset(md);
}
return rc;
}
int
main(int argc, char **argv)
{
int c;
const char *cfg_file, *output_file;
char cfg_path[255];
gfsec_secret_config_t *cfg;
gfsec_share_t *share;
unsigned have_shares, have_full;
pid_t pid;
gcry_md_hd_t md;
struct option options[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'v' },
{ "config", 1, NULL, 'c' },
{ "output", 1, NULL, 'o' },
{ NULL, 0, NULL, 0 }
};
setprogname(argv[0]);
cfg_file = output_file = NULL;
have_shares = have_full = 0;
while ( (c = getopt_long(argc, argv, "hvc:o:", options, NULL)) != -1 ) {
switch ( c ) {
case 'h':
usage(EXIT_SUCCESS);
break;
case '?':
usage(EXIT_FAILURE);
break;
case 'v':
info();
break;
case 'c':
cfg_file = optarg;
break;
case 'o':
output_file = optarg;
break;
}
}
gfsec_mtp_init();
if ( ! gcry_check_version(GCRYPT_VERSION) )
errx(EXIT_FAILURE, "libgcrypt version mismatch");
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
if ( get_config_file(cfg_file, cfg_path, sizeof(cfg_path)) == -1 )
err(EXIT_FAILURE, "Cannot find configuration file");
if ( ! (cfg = gfsec_read_config(cfg_path)) )
err(EXIT_FAILURE, "Cannot parse configuration file");
if ( ! output_file && ! (output_file = cfg->output_file) )
errx(EXIT_FAILURE, "No output file specified");
if ( gcry_md_open(&md, GCRY_MD_SHA256, 0) )
errx(EXIT_FAILURE, "Cannot create hashing context");
share = cfg->shares;
while ( share ) {
if ( get_share_data(share, md) == 0 ) {
if ( (share->flags & GFSEC_SHARE_FLAGS_FULL) > 0 )
have_full = 1;
else
have_shares += 1;
printf("Found share data in %s\n", get_share_display_name(share));
}
share = share->next;
}
gcry_md_close(md);
if ( have_full ) {
share = cfg->shares;
while ( share ) {
if ( share->data && (share->flags & GFSEC_SHARE_FLAGS_FULL) > 0 )
break;
share = share->next;
}
printf("Using full data in %s\n", get_share_display_name(share));
if ( write_file(output_file, share->data, share->length) == -1 )
err(EXIT_FAILURE, "Cannot write secret");
}
else if ( have_shares >= cfg->threshold ) {
gfshare_ctx *ctx;
unsigned char sharenrs[255], *secret;
unsigned n, size;
n = size = 0;
share = cfg->shares;
while ( share ) {
if ( share->data && share->share_nr > 0 ) {
sharenrs[n++] = share->share_nr;
if ( size == 0 )
size = share->length;
else if ( size != share->length )
errx(EXIT_FAILURE, "Shares have different sizes");
}
share = share->next;
}
if ( ! (secret = malloc(size)) || mlock(secret, size) == -1 )
err(EXIT_FAILURE, "Cannot allocated mlocked memory");
if ( ! (ctx = gfshare_ctx_init_dec(sharenrs, n, size)) )
errx(EXIT_FAILURE, "Cannot initialize libgfshare context");
n = 0;
share = cfg->shares;
while ( share ) {
if ( share->data && share->share_nr > 0 ) {
printf("using share data in %s\n", get_share_display_name(share));
gfshare_ctx_dec_giveshare(ctx, n++, share->data);
}
share = share->next;
}
gfshare_ctx_dec_extract(ctx, secret);
if ( write_file(output_file, secret, size) == -1 )
err(EXIT_FAILURE, "Cannot write secret");
memset(secret, 0, size);
munlock(secret, size);
free(secret);
}
else
errx(EXIT_FAILURE, "Not enough data to reconstitute secret");
if ( (pid = fork()) == -1 ) {
unlink(cfg->output_file);
err(EXIT_FAILURE, "Cannot fork");
}
else if ( pid == 0 ) {
gfsec_destroy_config(cfg);
if ( optind < argc ) {
execvp(argv[optind], &(argv[optind]));
}
else {
char *shell, *args[3];
if ( ! (shell = getenv("SHELL")) )
shell = "/bin/sh";
args[0] = shell;
args[1] = "-i";
args[2] = NULL;
putenv("PS1=gfsec> ");
execvp(shell, args);
}
err(EXIT_FAILURE, "Cannot exec");
}
else {
int status;
if ( waitpid(pid, &status, 0) == -1 )
warn("Cannot wait for child process");
printf("Removing secret.\n");
unlink(output_file);
}
gfsec_destroy_config(cfg);
return EXIT_SUCCESS;
}