/*
|
|
* gfsec - Secret sharing tools
|
|
* Copyright (C) 2016,2017,2020 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 <locale.h>
|
|
|
|
#include <getopt.h>
|
|
#include <err.h>
|
|
|
|
#include <gcrypt.h>
|
|
|
|
#include "util.h"
|
|
#include "secret.h"
|
|
#include "secretcfg.h"
|
|
#include "scheme-module.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\
|
|
-k, --keep Do not remove the reconstructed file\n\
|
|
once the command terminates.\n\
|
|
-o, --output FILE Write reconstructed secret in the specified\n\
|
|
file (override configuration file).\n"));
|
|
|
|
puts(_("\
|
|
-r, --restore-cmd CMD Execute the specified command instead of\n\
|
|
writing the reconstructed secret to disk.\n\
|
|
The secret will be send to the standard\n\
|
|
input of the command.\n\
|
|
-d, --destroy-cmd CMD Execute the specified command instead of\n\
|
|
deleting the reconstructed file.\n"));
|
|
|
|
printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
|
|
|
|
exit(status);
|
|
}
|
|
|
|
static void
|
|
info(void)
|
|
{
|
|
printf(_("\
|
|
gfsec-use (%s %s)\n\
|
|
Copyright (C) 2020 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")) && *env_info )
|
|
rc = snprintf(buffer, len, "%s/gfsecret/%s.conf", env_info, filename);
|
|
else if ( (env_info = getenv("HOME")) && *env_info )
|
|
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 int
|
|
get_share_data(gfsec_share_t *share)
|
|
{
|
|
int rc;
|
|
unsigned char *data;
|
|
char buffer[256];
|
|
size_t len;
|
|
|
|
rc = gfsec_scheme_module_get_file(share->scheme, share->authority,
|
|
share->path, &data, &len);
|
|
|
|
if ( rc == GFSEC_SCHEME_STATUS_SUCCESS ) {
|
|
gfsec_share_get_uri(share, buffer, sizeof(buffer));
|
|
|
|
if ( (rc = gfsec_share_set_data(share, data, len)) != 0 ) {
|
|
warnx(_("Cannot set data for share %s: %s"), buffer, gfsec_error_string(rc));
|
|
free(data);
|
|
}
|
|
else
|
|
printf(_("Found share data in %s\n"), buffer);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
delete_secret(const char *filename, const char *delete_cmd)
|
|
{
|
|
if ( delete_cmd ) {
|
|
int status;
|
|
|
|
status = system(delete_cmd);
|
|
if ( ! WIFEXITED(status) )
|
|
errx(EXIT_FAILURE, "Delete command terminated anormally");
|
|
}
|
|
else if ( unlink(filename) == -1 )
|
|
err(EXIT_FAILURE, "Cannot delete reconstructed file");
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int c, keep;
|
|
const char *cfg_file, *output_file, *restore_cmd, *delete_cmd;
|
|
char cfg_path[255];
|
|
gfsec_secret_t *cfg;
|
|
pid_t pid;
|
|
unsigned u;
|
|
|
|
struct option options[] = {
|
|
{ "help", 0, NULL, 'h' },
|
|
{ "version", 0, NULL, 'v' },
|
|
{ "config", 1, NULL, 'c' },
|
|
{ "keep", 0, NULL, 'k' },
|
|
{ "output", 1, NULL, 'o' },
|
|
{ "restore-cmd", 1, NULL, 'r' },
|
|
{ "destroy-cmd", 1, NULL, 'd' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
setprogname(argv[0]);
|
|
cfg_file = output_file = NULL;
|
|
restore_cmd = delete_cmd = NULL;
|
|
keep = 0;
|
|
|
|
setlocale(LC_ALL, "");
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
textdomain(PACKAGE);
|
|
|
|
while ( (c = getopt_long(argc, argv, "hvc:ko:r:d:",
|
|
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 'k':
|
|
keep = 1;
|
|
break;
|
|
|
|
case 'o':
|
|
output_file = optarg;
|
|
break;
|
|
|
|
case 'r':
|
|
restore_cmd = optarg;
|
|
break;
|
|
|
|
case 'd':
|
|
delete_cmd = optarg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gfsec_scheme_module_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 ( (c = gfsec_read_config(&cfg, cfg_path, &u)) != 0 ) {
|
|
if ( c == GFSEC_ERR_SYSTEM_ERROR )
|
|
err(EXIT_FAILURE, _("Cannot read configuration file"));
|
|
else
|
|
errx(EXIT_FAILURE, _("%s (line %u): %s"), cfg_path, u, gfsec_error_string(c));
|
|
}
|
|
|
|
if ( ! output_file &&
|
|
! restore_cmd &&
|
|
! (output_file = cfg->filename) &&
|
|
! (restore_cmd = cfg->restore) )
|
|
errx(EXIT_FAILURE, _("No output file or restore command specified"));
|
|
|
|
if ( ! delete_cmd )
|
|
delete_cmd = cfg->destroy;
|
|
|
|
if ( restore_cmd && ! delete_cmd )
|
|
keep = 1;
|
|
|
|
for ( u = 0; u < cfg->n_shares; u++ )
|
|
get_share_data(cfg->shares[u]);
|
|
|
|
if ( (c = gfsec_secret_can_combine(cfg)) != 0 )
|
|
errx(EXIT_FAILURE, _("Cannot reconstitute secret: %s"), gfsec_error_string(c));
|
|
|
|
if ( (c = gfsec_secret_combine(cfg)) != 0 )
|
|
errx(EXIT_FAILURE, _("Error while reconstituting secret: %s"), gfsec_error_string(c));
|
|
|
|
if ( restore_cmd ) {
|
|
FILE *out;
|
|
|
|
if ( ! (out = popen(restore_cmd, "w")) )
|
|
err(EXIT_FAILURE, _("Cannot execute command"));
|
|
|
|
if ( fwrite(cfg->data, 1, cfg->len, out) != cfg->len )
|
|
err(EXIT_FAILURE, _("Cannot send secret to command"));
|
|
|
|
pclose(out);
|
|
}
|
|
else if ( write_file(output_file, cfg->data, cfg->len) == -1 )
|
|
err(EXIT_FAILURE, _("Cannot write secret"));
|
|
|
|
if ( keep && argc >= optind ) {
|
|
/* If we keep the file at the end and there is no command to
|
|
* execute, we can leave here, there is no need to fork. */
|
|
gfsec_secret_free(cfg);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
if ( (pid = fork()) == -1 ) {
|
|
warn(_("Cannot fork"));
|
|
delete_secret(output_file, delete_cmd);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
else if ( pid == 0 ) {
|
|
gfsec_secret_free(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 if ( ! keep ) { /* No need to wait if we do not have to
|
|
remove the file at the end. */
|
|
int status;
|
|
|
|
if ( waitpid(pid, &status, 0) == -1 )
|
|
err(EXIT_FAILURE, _("Cannot wait for child process"));
|
|
|
|
if ( ! WIFEXITED(status) )
|
|
errx(EXIT_FAILURE, _("Child process terminated anormally"));
|
|
|
|
printf(_("Removing secret.\n"));
|
|
delete_secret(output_file, delete_cmd);
|
|
}
|
|
|
|
gfsec_secret_free(cfg);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|