Browse Source

Merge branch 'develop'

master v0.1.0
Damien Goutte-Gattat 6 years ago
parent
commit
fac8aa7535
  1. 4
      Makefile.am
  2. 69
      README
  3. 13
      configure.ac
  4. 2
      lib/Makefile.am
  5. 109
      lib/splitstr.c
  6. 35
      lib/splitstr.h
  7. 1
      man/Makefile.am
  8. 128
      man/gfsec-use.1.in
  9. 9
      src/Makefile.am
  10. 208
      src/gfsec-use.c
  11. 228
      src/mtp-support.c
  12. 42
      src/mtp-support.h
  13. 362
      src/secretcfg.c
  14. 45
      src/secretcfg.h
  15. 58
      src/share.c
  16. 56
      src/share.h
  17. 69
      src/util.c
  18. 12
      src/util.h
  19. 266
      src/uuid-support.c
  20. 38
      src/uuid-support.h

4
Makefile.am

@ -1,5 +1,5 @@
SUBDIRS = lib src
SUBDIRS = lib src man
ACLOCAL_AMFLAGS = -I m4 --install
dist_doc_DATA = AUTHORS COPYING
dist_doc_DATA = AUTHORS COPYING README

69
README

@ -0,0 +1,69 @@
Gfsecret - Secret sharing tools
===============================
Gfsecret is a set of tools to facilitate secret sharing according to the
Adi Shamir’s secret sharing scheme.
gfsec-use
---------
The gfsec-use program allows to temporarily reconstruct a splitted file
from shares that have been dispersed on several external devices (USB
sticks and/or MTP-compliant devices like some smartphones or audio
players).
The program needs a configuration file (by default,
$XDG_CONFIG_HOME/gfsecret/default.conf) which describes the shared
secret. Here is a sample configuration:
OUTFILE=/home/alice/mysecret
MINSHARES=2
URI=file:///home/alice/.local/share/gfsecret/mysecret.024
URI=label://MYSTICK/mysecretfile.070
URI=mtp://RF2GB6X704P/Documents/mysecret.139
With such a configuration, gfse-use will attempt to reconstruct the file
‘/home/alice/mysecret’, using at least two of the three available
shares: one available on the local filesystem, one on a USB storage
device with the label ‘MYSTICK’, and one on a MTP-compliant device with
the serial number ‘RF2GB6X704P’. Gfsec-use will automatically detect
which devices are currently connected and will fetch from them the
corresponding shares.
Once the secret file has been reconstructed (if enough shares are
available), gfsec-use will spawn a new shell (or any other program
specified on its command line). When the shell (or the user-specified
command) terminates, the reconstructed secret file will be automatically
deleted.
Install
-------
Gfsecret depends on the following libraries at compile-time:
– libgfshare <http://www.digital-scurf.org/software/libgfshare>, which
implements the secret sharing scheme proper (mandatory);
– GIO <http://developer.gnome.org/gio/>, to access shares stored on
external volumes (typically USB storage) (optional);
– libmtp <http://libmtp.sourceforge.net/>, to access shares stored on
MTP-compliant devices (optional).
Copying
-------
Gfsecret is distributed under the terms of the GNU General Public
License, version 3 or higher. The full license is included in the
COPYING file of the source distribution.
Homepage and contact
--------------------
The project is located at <http://www.incenp.org/dvlpt/gfsecret.html>.
The latest source code is available in a Git repository at
<git://git.incenp.org/gfsecret.git> and may also be browsed at
<http://www.incenp.org/gitweb/?p=gfsecret.git;a=summary>.
The author may be contacted at the following address:
Damien Goutte-Gattat <dgouttegattat@incenp.org>

13
configure.ac

@ -21,8 +21,19 @@ ICP_CHECK_NOTCH_FUNCS
dnl Check for Libgfshare
PKG_CHECK_MODULES([LIBGFSHARE], [libgfshare])
dnl Check for LibMTP
PKG_CHECK_MODULES([LIBMTP], [libmtp],
[AC_DEFINE([HAVE_LIBMTP], [1], [Define if libmtp is present.])],
[])
dnl Check for GIO
PKG_CHECK_MODULES([GIO], [gio-2.0 >= 2.22],
[AC_DEFINE([HAVE_GIO], [1], [Define if gio-2.0 is present.])],
[])
dnl Output files
AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile])
AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile
man/Makefile man/gfsec-use.1])
AC_OUTPUT
dnl Summary

2
lib/Makefile.am

@ -1,5 +1,5 @@
noinst_LIBRARIES = libgfsecret.a
libgfsecret_a_SOURCES = err.compat.h compat.h dummy.c
libgfsecret_a_SOURCES = err.compat.h compat.h splitstr.c splitstr.h
libgfsecret_a_LIBADD = $(LIBOBJS)

109
lib/splitstr.c

@ -0,0 +1,109 @@
/*
* splitstr - Incenp.org Notch Library: string-spliter module
* Copyright (C) 2011 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 <splitstr.h>
#include <errno.h>
#ifdef EXIT_ON_ENOMEM
#include <xmem.h>
#endif
/*
* The following call
*
* splitstr("to split this string", ' ', NULL);
*
* would return a pointer to a memory area whose contents would
* look like as depicted:
*
* 0 4 8 12 16 20 23 29 34
* +----+----+----+----+----+------+------------+----------+--------------+
* | 20 | 23 | 29 | 34 |NULL|t|o|\0|s|p|l|i|t|\0|t|h|i|s|\0|s|t|r|i|n|g|\0|
* +----+----+----+----+----+------+------------+----------+--------------+
*
* |<---------------------->|<------------------------------------------->|
* |(n + 1) * sizeof(char *)| n + l |
*
* with `n' being the number of tokens, and `l' the total count of
* characters in all tokens.
*
* The memory is allocated in a single block, so it can be freed by a
* single call to free().
*/
char **
splitstr(const char *str, char delim, size_t *ntoken)
{
const char *cursor;
char *copy, **index;
size_t n, l;
int is_token;
if ( ! str ) {
errno = EINVAL;
return NULL;
}
/* First parsing to compute the amount of memory needed. */
for ( n = l = is_token = 0, cursor = str; *cursor; cursor++ ) {
if ( *cursor != delim ) {
if ( ! is_token ) {
is_token = 1;
n += 1;
}
l += 1;
}
else
is_token = 0;
}
#ifdef EXIT_ON_ENOMEM
index = xmalloc((n + 1) * sizeof(char *) + l + n);
#else
if ( ! (index = malloc((n + 1) * sizeof(char *) + l + n)) )
return NULL;
#endif
copy = (char *)(index + (n + 1));
/* Second parsing to copy the tokens in the buffer. */
for ( n = l = is_token = 0, cursor = str; *cursor; cursor++ ) {
if ( *cursor != delim ) {
if ( ! is_token ) {
is_token = 1;
index[n++] = copy;
}
*copy++ = *cursor;
}
else {
is_token = 0;
if ( n && *(copy - 1) )
*copy++ = '\0';
}
}
*copy = '\0';
index[n] = NULL;
if ( ntoken )
*ntoken = n;
return index;
}

35
lib/splitstr.h

@ -0,0 +1,35 @@
/*
* splitstr - Incenp.org Notch Library: string-spliter module
* Copyright (C) 2011 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/>.
*/
#ifndef ICP20110203_SPLITSTR_H
#define ICP20110203_SPLITSTR_H
#include <stdlib.h>
#ifdef __cpluscplus
extern "C" {
#endif
char **
splitstr(const char *, char, size_t *);
#ifdef __cplusplus
}
#endif
#endif /* !ICP20110203_SPLITSTR_H */

1
man/Makefile.am

@ -0,0 +1 @@
man_MANS = gfsec-use.1

128
man/gfsec-use.1.in

@ -0,0 +1,128 @@
.TH GFSEC-USE 07/02/2016 "gfsecret @PACKAGE_VERSION@"
.SH NAME
gfsec-use \- Make use of a shared secret
.SH SYNOPSIS
.SY gfsec-use
.RB [ \-h | --help ]
.RB [ \-v | --version ]
.RB [ \-c | --config
.IR file ]
.RB [ \-o | --output
.IR file ]
.RB [ command... ]
.YS
.SH DESCRIPTION
.PP
.B gfsec-use
reconstructs a secret file that has been previously
splitted using a tool like
.BR gfsplit (1).
.PP
A user-specified command (or a shell) is spawn once
the secret has been reconstructed, and the secret
file is deleted when the command terminates.
.SH OPTIONS
.TP
.BR -h ", " --help
Display the help message.
.TP
.BR -v ", " --version
Display the version message.
.TP
.BR -c ", " --config " " \fIfile\fR
Specify a configuration file. If the specified
file does not exist, a .conf extension is appended
to the filename and a corresponding file is searched
in $XDG_CONFIG_HOME/gfsecret. When that option is
not used, a default configuration
$XDG_CONFIG_HOME/gfsecret/default.conf is assumed.
.TP
.BR -o ", " --output " " \fIfile\fR
Write the reconstructed secret in the specified
file. This overrides the OUTFILE parameter in the
configuration file.
.SH CONFIGURATION FILE
.PP
A configuration file describes one secret file to reconstruct.
Blank lines and lines starting with a # character are ignored.
.PP
The following directives can be used:
.TP
OUTFILE=\fIfile\fR
Specify the file to write the reconstructed secret into.
.TP
MINSHARES=\fIn\fR
Specify the minimal number of shares needed to reconstruct
the secret. The default if unspecified is 2.
.TP
URI=\fIuri\fR
Specify an URI indicating where to find a share.
.PP
Supported URI schemes are:
.TP
file:///
Indicates a file on the local filesystem.
.TP
uuid://\fIuuid\fR/
Indicates a file on the external volume identified
by the specified UUID.
.TP
label://\fIlabel\fR/
Indicates a file on the external volume identified
by the specified label.
.TP
mtp://\fIserial\fR/
Indicates a file on the MTP device identified by the
specified serial number.
.PP
Whatever the scheme, the file part of the URI must end
with an extension indicating the share number, as
generated by
.BR gfsplit (1).
.PP
The URI may include a \fIshare=no\fR parameter,
indicating that the corresponding file contains the
whole secret and not only a share (in that case, the
previous remark about the share number in the extension
does not apply).
.SH EXAMPLE CONFIGURATION FILE
.PP
.nf
OUTFILE=/home/alice/mysecret
MINSHARES=2
URI=file:///home/alice/.local/share/gfsecret/mysecret.024
URI=label://USBSTICK/mysecret.070
URI=mtp://RF2GB6X704P/Documents/mysecret.139
.fi
.SH REPORTING BUGS
.PP
Report bugs to
.MT @PACKAGE_BUGREPORT@
Damien Goutte-Gattat
.ME .
.SH SEE ALSO
.BR gfsplit (1),
.BR gfcombine (1),
.BR libgfshare (3),
.BR gfshare (7)
.SH COPYRIGHT
.ad l
.PP
Copyright \(co 2016 Damien Goutte-Gattat
.PP
This program is released under the GNU General Public License.
See the COPYING file in the source distribution or
.UR http://www.gnu.org/licenses/gpl.html
.UE .

9
src/Makefile.am

@ -1,7 +1,10 @@
bin_PROGRAMS = gfsec-use
gfsec_use_SOURCES = gfsec-use.c util.c util.h
gfsec_use_SOURCES = gfsec-use.c util.c util.h share.c share.h \
secretcfg.c secretcfg.h \
mtp-support.c mtp-support.h \
uuid-support.c uuid-support.h
AM_CPPFLAGS = -I$(top_srcdir)/lib
AM_LDFLAGS = -L$(top_builddir)/lib
AM_CPPFLAGS = -I$(top_srcdir)/lib $(LIBGFSHARE_CFLAGS) $(LIBMTP_CFLAGS) $(GIO_CFLAGS)
AM_LDFLAGS = -L$(top_builddir)/lib $(LIBGFSHARE_LIBS) $(LIBMTP_LIBS) $(GIO_LIBS)
LDADD = -lgfsecret

208
src/gfsec-use.c

@ -24,11 +24,21 @@
#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 "util.h"
#include "share.h"
#include "secretcfg.h"
#include "mtp-support.h"
#include "uuid-support.h"
static void
usage(int status)
@ -44,7 +54,9 @@ secret is destroyed when the command or the shell terminates.\n");
puts("\
-c, --config FILE Read configuration from the specified\n\
file.\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);
@ -72,15 +84,15 @@ get_config_file_in_dir(const char *filename, char *buffer, size_t len)
const char *env_info;
if ( (env_info = getenv("XDG_CONFIG_HOME")) )
rc = snprintf(buffer, len, "%s/gfsecret/%s", env_info, filename);
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", env_info, filename);
rc = snprintf(buffer, len, "%s/.config/gfsecret/%s.conf", env_info, filename);
else {
rc = -1;
errno = ENOENT;
}
if ( rc >= len ) {
if ( rc >= 0 && (unsigned)rc >= len ) {
rc = -1;
errno = ENAMETOOLONG;
}
@ -96,7 +108,7 @@ get_config_file(const char *filename, char *buffer, size_t len)
int rc;
if ( ! filename )
rc = get_config_file_in_dir("default.conf", buffer, len);
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 ) {
@ -111,24 +123,77 @@ get_config_file(const char *filename, char *buffer, size_t len)
return rc;
}
static int
get_share_data(gfsec_share_t *share)
{
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;
}
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;
}
int
main(int argc, char **argv)
{
int c;
const char *cfg_file;
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;
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 = NULL;
cfg_file = output_file = NULL;
have_shares = have_full = 0;
while ( (c = getopt_long(argc, argv, "hvc:", options, NULL)) != -1 ) {
while ( (c = getopt_long(argc, argv, "hvc:o:", options, NULL)) != -1 ) {
switch ( c ) {
case 'h':
usage(EXIT_SUCCESS);
@ -145,11 +210,138 @@ main(int argc, char **argv)
case 'c':
cfg_file = optarg;
break;
case 'o':
output_file = optarg;
break;
}
}
gfsec_mtp_init();
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");
share = cfg->shares;
while ( share ) {
if ( get_share_data(share) == 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;
}
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;
}

228
src/mtp-support.c

@ -0,0 +1,228 @@
/*
* 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 "mtp-support.h"
#ifndef HAVE_LIBMTP
#include <errno.h>
unsigned char *
gfsec_get_file_from_mtp(const char *authority,
const char *path,
size_t *len)
{
(void) authority;
(void) path;
(void) len;
errno = ENOSYS;
return NULL;
}
#else
#include <string.h>
#include <splitstr.h>
#define MTP_ROOT_FOLDER_ID 0xFFFFFFFF
/**
* Finds a connected MTP device with the specified serial number.
*
* @param[in] wanted_serial The serial number of the device to find.
*
* @return An object representing the found device (to be freed with
* LIBMTP_Release_Device), or NULL if no device was found.
*/
static LIBMTP_mtpdevice_t *
find_mtp_device(const char *wanted_serial)
{
LIBMTP_raw_device_t *raw_devices;
LIBMTP_mtpdevice_t *device;
int i, n;
if ( LIBMTP_Detect_Raw_Devices(&raw_devices, &n) != 0 )
return NULL;
for ( i = 0, device = NULL; i < n && device == NULL; i++ ) {
char *serial;
device = LIBMTP_Open_Raw_Device_Uncached(&(raw_devices[i]));
if ( (serial = LIBMTP_Get_Serialnumber(device)) == NULL ||
strcmp(serial, wanted_serial) != 0 ) {
LIBMTP_Release_Device(device);
device = NULL;
if ( serial )
free(serial);
}
}
free(raw_devices);
return device;
}
/**
* Finds a file on a given MTP device.
*
* @param[in] device The MTP device.
* @param[in] storage Storage identifier (0 to search in all storages).
* @param[in] path The path of the file to find.
* @param[out] size If not NULL and the file is found, this will be set
* to the size of the file.
*
* @return The MTP identifier for the file if found, or -1 if not found.
*/
static long
find_mtp_file(LIBMTP_mtpdevice_t *device,
unsigned storage,
const char *path,
size_t *size)
{
char **components;
size_t n;
unsigned i, file_id;
long rc = -1;
if ( ! (components = splitstr(path, '/', &n)) )
return -1;
for ( i = 0, file_id = MTP_ROOT_FOLDER_ID; i < n; i++ ) {
LIBMTP_file_t *files, *file;
int found = 0;
files = LIBMTP_Get_Files_And_Folders(device, storage, file_id);
file = files;
while ( file && ! found ) {
LIBMTP_file_t *tmp;
if ( strcmp(file->filename, components[i]) == 0 ) {
if ( file->filetype == LIBMTP_FILETYPE_FOLDER && i < n - 1 ) {
file_id = file->item_id;
found = 1;
}
else if ( file->filetype != LIBMTP_FILETYPE_FOLDER && i == n - 1 ) {
rc = file->item_id;
found = 1;
if ( size )
*size = file->filesize;
}
}
tmp = file;
file = file->next;
LIBMTP_destroy_file_t(tmp);
}
if ( ! found )
i = n; /* Exit loop. */
}
free(components);
return rc;
}
struct getfile_cb_data {
unsigned char *buffer;
unsigned written;
size_t expected;
};
/**
* Callback for the LIBMTP_Get_File_To_Handler function.
*
* @param params MTP parameters (unused).
* @param priv Pointer to a getfile_cb_data structure.
* @param sendlen Number of bytes available.
* @param data Actual bytes available.
* @param[out] putlen Number of bytes used.
*
* @return LIBMTP_HANLDER_RETURN_OK if successful, or
* LIBMTP_HANDLER_RETURN_ERROR if we get more bytes than we expected.
*/
static unsigned short
getfile_cb(void *params,
void *priv,
unsigned sendlen,
unsigned char *data,
unsigned *putlen)
{
struct getfile_cb_data *cb_data = priv;
(void) params;
if ( cb_data->written + sendlen > cb_data->expected )
return LIBMTP_HANDLER_RETURN_ERROR;
memcpy(cb_data->buffer + cb_data->written, data, sendlen);
cb_data->written += sendlen;
*putlen = sendlen;
return LIBMTP_HANDLER_RETURN_OK;
}
/**
* Gets the contents of a file on a MTP device.
*
* @param[in] authority Serial number of the MTP device.
* @param[in] path Path to the file on the MTP device.
* @param[out] len Number of bytes read from the device.
*
* @return A pointer to a newly allocated buffer containing
* the file data, or NULL if an error occured.
*/
unsigned char *
gfsec_get_file_from_mtp(const char *authority,
const char *path,
size_t *len)
{
LIBMTP_mtpdevice_t *device;
struct getfile_cb_data cb_data;
unsigned char *blob = NULL;
if ( (device = find_mtp_device(authority)) ) {
long file_id;
if ( (file_id = find_mtp_file(device, 0, path, len)) != -1 ) {
if ( (blob = malloc(*len)) ) {
cb_data.buffer = blob;
cb_data.written = 0;
cb_data.expected = *len;
if ( LIBMTP_Get_File_To_Handler(device, file_id,
getfile_cb, &cb_data, NULL, NULL) != 0 ) {
free(blob);
blob = NULL;
}
}
}
LIBMTP_Release_Device(device);
}
return blob;
}
#endif /* HAVE_LIBMTP */

42
src/mtp-support.h

@ -0,0 +1,42 @@
/*
* 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/>.
*/
#ifndef ICP20160207_MTP_SUPPORT_H
#define ICP20160207_MTP_SUPPORT_H
#include <stdlib.h>
#ifdef __cpluscplus
extern "C" {
#endif
#ifdef HAVE_LIBMTP
#include <libmtp.h>
#define gfsec_mtp_init() LIBMTP_Init()
#else
#define gfsec_mtp_init()
#endif
unsigned char *
gfsec_get_file_from_mtp(const char *, const char *, size_t *);
#ifdef __cplusplus
}
#endif
#endif /* !ICP20160207_MTP_SUPPORT_H */

362
src/secretcfg.c

@ -0,0 +1,362 @@
/*
* 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 "secretcfg.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define MAX_LINE_LENGTH 256
/**
* Reads a single line from the specified stream.
* This function differs from standard fgets(3) in two aspects: the
* newline character is not stored, and if the line to read is too long
* to fit into the provided buffer, the whole line is discarded.
*
* @param f The stream to read from.
* @param buffer A character buffer to store the line into.
* @param len Size of the \a buffer array.
*
* @return The number of characters read, or -1 if an error occured.
* If the line was too long, -1 is returned and errno is set to
* EMSGSIZE.
*/
static ssize_t
get_line(FILE *f, char *buffer, size_t len)
{
int c;
size_t n = 0;
if ( ! f || ! buffer ) {
errno = EINVAL;
return -1;
}
while ( (c = fgetc(f)) != '\n' ) {
if ( c == EOF )
return -1;
if ( n >= len - 1 ) { /* Line too long */
errno = EMSGSIZE;
while ( (c = fgetc(f)) != '\n' && c != EOF ) ; /* Discard line */
return -1;
}
buffer[n++] = (char) c;
}
buffer[n] = '\0';
return n;
}
/**
* Reads the provided string up to the first '/' character.
* The pointer is advanced to that character.
*
* @return A newly allocated copy of the part of the string
* before the '/' character. If no '/' character was found,
* NULL is returned and errno is set to EBADMSG.
*/
static char *
parse_authority(const char **uri)
{
char *slash, *authority = NULL;
if ( ! uri ) {
errno = EBADMSG;
return NULL;
}
if ( (slash = strchr(*uri, '/')) ) {
authority = strndup(*uri, slash - *uri);
*uri = slash;
}
else
errno = EBADMSG;
return authority;
}
/**
* Reads the provided string up to its end or up to the first
* '?' character (marking the end of the 'path' part of an URI).
* The pointer is advanced to that character, or to the
* terminating NULL byte.
*
* @return A newly allocated copy of the part of the string
* before the '?' character (or the whole string if no '?'
* was found).
*/
static char *
parse_path(const char **uri)
{
char *qm, *path = NULL;
if ( ! uri ) {
errno = EBADMSG;
return NULL;
}
qm = strchrnul(*uri, '?');
path = strndup(*uri, qm - *uri);
*uri = qm;
return path;
}
/**
* Parses a name=value parameter in the provided string.
* Advances the pointer to the next character after the
* value.
*
* @return 0 if a correctly formed name=value pair was
* found. Otherwise, -1 is returned and errno is set to
* EBADMSG.
*/
static int
parse_parameter(const char **uri, gfsec_share_t *share)
{
char *eq, *amp;
int rc = 0;
if ( **uri == '?' || **uri == '&' )
*uri += 1; /* Skip initial delimiter. */
if ( **uri == '\0' )
return 0; /* Silently ignore terminal delimiter. */
if ( ! (eq = strchr(*uri, '=')) ) {
errno = EBADMSG;
return -1;
}
if ( strncmp(*uri, "share", eq - *uri) == 0 ) {
amp = strchrnul(++eq, '&');
if ( strncmp(eq, "no", amp - eq) == 0 )
share->flags |= GFSEC_SHARE_FLAGS_FULL;
*uri = amp;
}
else { /* Skip unknown parameter. */
while ( **uri != '\0' && **uri != '&' )
(*uri) += 1;
}
return rc;
}
/**
* Extract the share number from the last 3 characters of the
* specified path.
*
* @return The share number, or 0 if the path does not end
* with a correctly formatted share number.
*/
static unsigned char
get_share_number_from_path(const char *path)
{
size_t len;
unsigned long sharenr;
char *endptr;
len = strlen(path);
if ( len < 4 )
return 0;
if ( path[len - 4] != '.' )
return 0;
sharenr = strtoul(&(path[len - 3]), &endptr, 10);
if ( *endptr != '\0' )
return 0;
if ( sharenr > 255 )
return 0;
return (unsigned char) sharenr;
}
/**
* Parses the specified URI into a share object.
*
* @return 0 if the URI was successfully parsed; -1 if an error
* occured or if the URI is malformed (in that last case, errno
* is set to EBADMSG).
*/
static int
parse_uri(const char *uri, gfsec_secret_config_t *cfg)
{
gfsec_share_t *share;
const char *p = uri;
if ( ! uri || ! cfg ) {
errno = EINVAL;
return -1;
}
if ( ! (share = malloc(sizeof(*share))) )
return -1;
share->next = NULL;
share->authority = NULL;
share->path = NULL;
share->share_nr = 0;
share->flags = GFSEC_SHARE_FLAGS_NONE;
share->length = 0;
share->data = NULL;
if ( strncmp(p, "file://", 7) == 0 && (p += 7) )
share->scheme = SCHEME_FILE;
else if ( strncmp(p, "uuid://", 7) == 0 && (p += 7) )
share->scheme = SCHEME_UUID;
else if ( strncmp(p, "label://", 8) == 0 && (p += 8) )
share->scheme = SCHEME_LABEL;
else if ( strncmp(p, "mtp://", 6) == 0 && (p += 6) )
share->scheme = SCHEME_MTP;
else {
errno = EBADMSG;
goto bad_uri;
}
if ( ! (share->authority = parse_authority(&p)) )
goto bad_uri;
if ( ! (share->path = parse_path(&p)) )
goto bad_uri;
while ( *p ) {
if ( parse_parameter(&p, share) == -1 )
goto bad_uri;
}
if ( (share->flags & GFSEC_SHARE_FLAGS_FULL) == 0 ) {
if ( (share->share_nr = get_share_number_from_path(share->path)) == 0 ) {
errno = EBADMSG;
goto bad_uri;
}
}
if ( cfg->shares ) {
gfsec_share_t *cursor = cfg->shares;
while ( cursor->next )
cursor = cursor->next;
cursor->next = share;
}
else
cfg->shares = share;
cfg->n_shares += 1;
return 0;
bad_uri:
gfsec_destroy_share(share, 0);
return -1;
}
/**
* Parses the specified configuration file.
*
* @return A newly allocated configuration object, or NULL if
* an error occured. If the configuration file contains an
* error, errno is set to EBADMSG.
*/
gfsec_secret_config_t *
gfsec_read_config(const char *filename)
{
FILE *f;
char buffer[MAX_LINE_LENGTH];
gfsec_secret_config_t *cfg;
int rc;
if ( ! filename ) {
errno = EINVAL;
return NULL;
}
if ( ! (f = fopen(filename, "r")) )
return NULL;
if ( ! (cfg = malloc(sizeof(*cfg))) ) {
fclose(f);
return NULL;
}
cfg->output_file = NULL;
cfg->shares = NULL;
cfg->threshold = 2;
rc = 0;
while ( rc >= 0 ) {
ssize_t n;
n = get_line(f, buffer, sizeof(buffer));
if ( n < 0 ) {
rc = feof(f) ? 0 : -1;
break;
}
else if ( n == 0 || buffer[0] == '#' )
; /* Comment or empty line */
else if ( strncmp("OUTFILE=", buffer, 8) == 0 ) {
if ( ! (cfg->output_file = strdup(buffer + 8)) )
rc = -1;
}
else if ( strncmp("MINSHARES=", buffer, 10) == 0 ) {
char *endptr;
unsigned long l;
l = strtoul(buffer + 10, &endptr, 10);
if ( *endptr != '\0' || l < 2 || l > 255 )
rc = -1, errno = EBADMSG;
else
cfg->threshold = (unsigned char) l;
}
else if ( strncmp("URI=", buffer, 4) == 0 ) {
if ( parse_uri(&(buffer[4]), cfg) == -1 )
rc = -1;
}
}
fclose(f);
if ( rc == -1 ) {
gfsec_destroy_config(cfg);
cfg = NULL;
}
return cfg;
}
/**
* Frees the specified configuration object.
*/
void
gfsec_destroy_config(gfsec_secret_config_t *cfg)
{
if ( cfg ) {
if ( cfg->output_file )
free(cfg->output_file);
gfsec_destroy_share(cfg->shares, 1);
free(cfg);
}
}

45
src/secretcfg.h

@ -0,0 +1,45 @@
/*
* secretcfg - 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/>.
*/
#ifndef ICP20160206_SECRETCFG_H
#define ICP20160206_SECRETCFG_H
#include "share.h"
typedef struct gfsec_secret_config {
char *output_file;
gfsec_share_t *shares;
unsigned char n_shares;
unsigned char threshold;
} gfsec_secret_config_t;
#ifdef __cpluscplus
extern "C" {
#endif
gfsec_secret_config_t *
gfsec_read_config(const char *);
void
gfsec_destroy_config(gfsec_secret_config_t *);
#ifdef __cplusplus
}
#endif
#endif /* !ICP20160206_SECRETCFG_H */

58
src/share.c

@ -0,0 +1,58 @@
/*
* 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 "share.h"
#include <string.h>
/**
* Frees the specified share object.
*
* @param share The share object to destroy.
* @param linked If non-zero, also free all linked share objects.
*/
void
gfsec_destroy_share(gfsec_share_t *share, int linked)
{
while ( share ) {
if ( share->authority )
free(share->authority);
if ( share->path )
free(share->path);
if ( share->data ) {
memset(share->data, 0, share->length);
free(share->data);
}
if ( linked ) {
gfsec_share_t *tmp = share->next;
free(share);
share = tmp;
}
else {
free(share);
share = NULL;
}
}
}

56
src/share.h

@ -0,0 +1,56 @@
/*
* 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/>.
*/
#ifndef ICP20160206_SHARE_H
#define ICP20160206_SHARE_H
#include <stdlib.h>
#define GFSEC_SHARE_FLAGS_NONE 0x00
#define GFSEC_SHARE_FLAGS_FULL 0x01
typedef enum gfsec_scheme {
SCHEME_FILE = 0,
SCHEME_UUID,
SCHEME_LABEL,
SCHEME_MTP
} gfsec_scheme_t;
typedef struct gfsec_share {
struct gfsec_share *next;
gfsec_scheme_t scheme;
char *authority;
char *path;
unsigned char share_nr;
unsigned flags;
size_t length;
unsigned char *data;
} gfsec_share_t;
#ifdef __cpluscplus
extern "C" {
#endif
void
gfsec_destroy_share(gfsec_share_t *, int);
#ifdef __cplusplus
}
#endif
#endif /* !ICP20160206_SHARE_H */

69
src/util.c

@ -40,3 +40,72 @@ file_exists(const char *path)
return stat(path, &st);
}
long
get_file_size(FILE *f)
{
long s;
if ( ! f ) {
errno = EINVAL;
return -1;
}
if ( fseek(f, 0, SEEK_END) == -1
|| (s = ftell(f)) == -1
|| fseek(f, 0, SEEK_SET) == -1 )
s = -1;
return s;
}
unsigned char *
read_file(const char *filename, size_t *len)
{
FILE *f;
unsigned char *blob = NULL;
if ( ! filename ) {
errno = EINVAL;
return NULL;
}
if ( (f = fopen(filename, "r")) ) {
long size;
size_t nread;
if ( (size = get_file_size(f)) != -1 ) {
if ( (blob = malloc(size)) ) {
if ( (nread = fread(blob, 1, size, f)) < (unsigned long) size ) {
free(blob);
blob = NULL;
}
else if ( len )
*len = nread;
}
}
fclose(f);
}
return blob;
}
int
write_file(const char *filename, const unsigned char *contents, size_t len)
{
FILE *f;
int rc;
if ( ! filename || ! contents ) {
errno = EINVAL;
return -1;
}
if ( (f = fopen(filename, "w")) ) {
rc = fwrite(contents, sizeof(char), len, f) == len ? 0 : -1;
fclose(f);
}
return rc;
}

12
src/util.h

@ -19,6 +19,9 @@
#ifndef ICP20160205_UTIL_H
#define ICP20160205_UTIL_H
#include <stdio.h>
#include <stdlib.h>
#ifdef __cpluscplus
extern "C" {
#endif
@ -26,6 +29,15 @@ extern "C" {
int
file_exists(const char *);
long
get_file_size(FILE *f);
unsigned char *
read_file(const char *, size_t *);
int
write_file(const char *, const unsigned char *, size_t);
#ifdef __cplusplus
}
#endif