2010-06-07 13:33:02 +00:00
|
|
|
|
/* gpgtar.c - A simple TAR implementation mainly useful for Windows.
|
|
|
|
|
* Copyright (C) 2010 Free Software Foundation, Inc.
|
2020-12-04 11:51:48 +01:00
|
|
|
|
* Copyright (C) 2020 g10 Code GmbH
|
2010-06-07 13:33:02 +00:00
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG 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.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG 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
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2020-12-04 11:51:48 +01:00
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
2010-06-07 13:33:02 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* GnuPG comes with a shell script gpg-zip which creates archive files
|
|
|
|
|
in the same format as PGP Zip, which is actually a USTAR format.
|
|
|
|
|
That is fine and works nicely on all Unices but for Windows we
|
|
|
|
|
don't have a compatible shell and the supply of tar programs is
|
|
|
|
|
limited. Given that we need just a few tar option and it is an
|
|
|
|
|
open question how many Unix concepts are to be mapped to Windows,
|
|
|
|
|
we might as well write our own little tar customized for use with
|
|
|
|
|
gpg. So here we go. */
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
2015-11-26 14:08:48 +01:00
|
|
|
|
#include <ctype.h>
|
2010-06-07 13:33:02 +00:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2020-02-10 16:37:34 +01:00
|
|
|
|
#define INCLUDED_BY_MAIN_MODULE 1
|
2017-03-07 20:21:23 +09:00
|
|
|
|
#include "../common/util.h"
|
|
|
|
|
#include "../common/i18n.h"
|
|
|
|
|
#include "../common/sysutils.h"
|
2010-06-07 13:33:02 +00:00
|
|
|
|
#include "../common/openpgpdefs.h"
|
2012-02-06 20:50:47 +01:00
|
|
|
|
#include "../common/init.h"
|
2015-11-26 14:08:48 +01:00
|
|
|
|
#include "../common/strlist.h"
|
2010-06-07 13:33:02 +00:00
|
|
|
|
|
|
|
|
|
#include "gpgtar.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Constants to identify the commands and options. */
|
|
|
|
|
enum cmd_and_opt_values
|
|
|
|
|
{
|
|
|
|
|
aNull = 0,
|
2016-07-05 14:29:29 +02:00
|
|
|
|
aCreate = 600,
|
|
|
|
|
aExtract,
|
2010-06-07 13:33:02 +00:00
|
|
|
|
aEncrypt = 'e',
|
|
|
|
|
aDecrypt = 'd',
|
|
|
|
|
aSign = 's',
|
2010-08-25 10:31:47 +00:00
|
|
|
|
aList = 't',
|
2010-06-07 13:33:02 +00:00
|
|
|
|
|
|
|
|
|
oSymmetric = 'c',
|
|
|
|
|
oRecipient = 'r',
|
|
|
|
|
oUser = 'u',
|
|
|
|
|
oOutput = 'o',
|
2015-11-25 18:29:22 +01:00
|
|
|
|
oDirectory = 'C',
|
2010-06-07 13:33:02 +00:00
|
|
|
|
oQuiet = 'q',
|
|
|
|
|
oVerbose = 'v',
|
2010-08-09 09:05:18 +00:00
|
|
|
|
oFilesFrom = 'T',
|
2010-06-07 13:33:02 +00:00
|
|
|
|
oNoVerbose = 500,
|
|
|
|
|
|
|
|
|
|
aSignEncrypt,
|
2015-11-25 14:57:14 +01:00
|
|
|
|
oGpgProgram,
|
2010-06-07 13:33:02 +00:00
|
|
|
|
oSkipCrypto,
|
2010-08-09 09:36:49 +00:00
|
|
|
|
oOpenPGP,
|
|
|
|
|
oCMS,
|
2010-07-16 13:19:45 +00:00
|
|
|
|
oSetFilename,
|
2015-11-26 14:08:48 +01:00
|
|
|
|
oNull,
|
2020-08-21 21:28:28 +02:00
|
|
|
|
oUtf8Strings,
|
2023-06-15 13:52:18 +02:00
|
|
|
|
oNoCompress,
|
2015-11-26 14:08:48 +01:00
|
|
|
|
|
2022-03-21 13:06:00 +01:00
|
|
|
|
oBatch,
|
|
|
|
|
oAnswerYes,
|
|
|
|
|
oAnswerNo,
|
|
|
|
|
oStatusFD,
|
|
|
|
|
oRequireCompliance,
|
2022-03-22 10:19:55 +01:00
|
|
|
|
oWithLog,
|
2022-03-21 13:06:00 +01:00
|
|
|
|
|
2015-11-26 14:08:48 +01:00
|
|
|
|
/* Compatibility with gpg-zip. */
|
2015-11-26 15:36:52 +01:00
|
|
|
|
oGpgArgs,
|
2015-11-26 14:08:48 +01:00
|
|
|
|
oTarArgs,
|
2015-11-26 17:05:12 +01:00
|
|
|
|
|
|
|
|
|
/* Debugging. */
|
2022-03-21 13:06:00 +01:00
|
|
|
|
oDryRun
|
2010-06-07 13:33:02 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The list of commands and options. */
|
|
|
|
|
static ARGPARSE_OPTS opts[] = {
|
|
|
|
|
ARGPARSE_group (300, N_("@Commands:\n ")),
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2016-07-05 14:29:29 +02:00
|
|
|
|
ARGPARSE_c (aCreate, "create", N_("create an archive")),
|
|
|
|
|
ARGPARSE_c (aExtract, "extract", N_("extract an archive")),
|
|
|
|
|
ARGPARSE_c (aEncrypt, "encrypt", N_("create an encrypted archive")),
|
|
|
|
|
ARGPARSE_c (aDecrypt, "decrypt", N_("extract an encrypted archive")),
|
2010-06-07 13:33:02 +00:00
|
|
|
|
ARGPARSE_c (aSign, "sign", N_("create a signed archive")),
|
|
|
|
|
ARGPARSE_c (aList, "list-archive", N_("list an archive")),
|
|
|
|
|
|
|
|
|
|
ARGPARSE_group (301, N_("@\nOptions:\n ")),
|
|
|
|
|
|
|
|
|
|
ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
|
|
|
|
|
ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
|
|
|
|
|
ARGPARSE_s_s (oUser, "local-user",
|
|
|
|
|
N_("|USER-ID|use USER-ID to sign or decrypt")),
|
|
|
|
|
ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
|
|
|
|
|
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
|
|
|
|
|
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
|
2015-11-25 14:57:14 +01:00
|
|
|
|
ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
|
2010-06-07 13:33:02 +00:00
|
|
|
|
ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
|
2015-11-26 17:05:12 +01:00
|
|
|
|
ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
|
2010-07-16 13:19:45 +00:00
|
|
|
|
ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
|
2015-11-26 14:08:48 +01:00
|
|
|
|
ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
|
|
|
|
|
ARGPARSE_s_n (oCMS, "cms", "@"),
|
2023-06-15 13:52:18 +02:00
|
|
|
|
ARGPARSE_s_n (oNoCompress, "no-compress", "@"),
|
2015-11-26 14:08:48 +01:00
|
|
|
|
|
2022-03-21 13:06:00 +01:00
|
|
|
|
ARGPARSE_s_n (oBatch, "batch", "@"),
|
|
|
|
|
ARGPARSE_s_n (oAnswerYes, "yes", "@"),
|
|
|
|
|
ARGPARSE_s_n (oAnswerNo, "no", "@"),
|
|
|
|
|
ARGPARSE_s_i (oStatusFD, "status-fd", "@"),
|
|
|
|
|
ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"),
|
2022-03-22 10:19:55 +01:00
|
|
|
|
ARGPARSE_s_n (oWithLog, "with-log", "@"),
|
2022-03-21 13:06:00 +01:00
|
|
|
|
|
2015-11-26 14:08:48 +01:00
|
|
|
|
ARGPARSE_group (302, N_("@\nTar options:\n ")),
|
|
|
|
|
|
|
|
|
|
ARGPARSE_s_s (oDirectory, "directory",
|
2019-03-06 20:08:26 +01:00
|
|
|
|
N_("|DIRECTORY|change to DIRECTORY first")),
|
2015-11-26 14:08:48 +01:00
|
|
|
|
ARGPARSE_s_s (oFilesFrom, "files-from",
|
|
|
|
|
N_("|FILE|get names to create from FILE")),
|
|
|
|
|
ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
|
2020-08-21 21:28:28 +02:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
ARGPARSE_s_n (oUtf8Strings, "utf8-strings",
|
|
|
|
|
N_("-T reads UTF-8 encoded names")),
|
|
|
|
|
#else
|
|
|
|
|
ARGPARSE_s_n (oUtf8Strings, "utf8-strings", "@"),
|
|
|
|
|
#endif
|
2015-11-26 14:08:48 +01:00
|
|
|
|
|
2015-11-26 15:36:52 +01:00
|
|
|
|
ARGPARSE_s_s (oGpgArgs, "gpg-args", "@"),
|
2015-11-26 14:08:48 +01:00
|
|
|
|
ARGPARSE_s_s (oTarArgs, "tar-args", "@"),
|
|
|
|
|
|
|
|
|
|
ARGPARSE_end ()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The list of commands and options for tar that we understand. */
|
|
|
|
|
static ARGPARSE_OPTS tar_opts[] = {
|
|
|
|
|
ARGPARSE_s_s (oDirectory, "directory",
|
|
|
|
|
N_("|DIRECTORY|extract files into DIRECTORY")),
|
2010-08-09 09:05:18 +00:00
|
|
|
|
ARGPARSE_s_s (oFilesFrom, "files-from",
|
|
|
|
|
N_("|FILE|get names to create from FILE")),
|
|
|
|
|
ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
|
2010-06-07 13:33:02 +00:00
|
|
|
|
|
|
|
|
|
ARGPARSE_end ()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2019-03-06 17:46:40 +01:00
|
|
|
|
/* Global flags. */
|
2020-08-20 15:50:50 +02:00
|
|
|
|
static enum cmd_and_opt_values cmd = 0;
|
|
|
|
|
static int skip_crypto = 0;
|
|
|
|
|
static const char *files_from = NULL;
|
|
|
|
|
static int null_names = 0;
|
2019-03-06 17:46:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
|
2017-02-20 16:19:50 -05:00
|
|
|
|
/* Print usage information and provide strings for help. */
|
2010-06-07 13:33:02 +00:00
|
|
|
|
static const char *
|
|
|
|
|
my_strusage( int level )
|
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
|
|
switch (level)
|
|
|
|
|
{
|
2020-12-04 11:51:48 +01:00
|
|
|
|
case 9: p = "GPL-3.0-or-later"; break;
|
2013-11-18 14:09:47 +01:00
|
|
|
|
case 11: p = "@GPGTAR@ (@GNUPG@)";
|
2010-06-07 13:33:02 +00:00
|
|
|
|
break;
|
|
|
|
|
case 13: p = VERSION; break;
|
2020-12-04 11:51:48 +01:00
|
|
|
|
case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
|
2010-06-07 13:33:02 +00:00
|
|
|
|
case 17: p = PRINTABLE_OS_NAME; break;
|
|
|
|
|
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
|
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
case 40:
|
|
|
|
|
p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
|
|
|
|
|
break;
|
|
|
|
|
case 41:
|
|
|
|
|
p = _("Syntax: gpgtar [options] [files] [directories]\n"
|
|
|
|
|
"Encrypt or sign files into an archive\n");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default: p = NULL; break;
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
|
|
|
|
|
{
|
2019-03-06 17:46:40 +01:00
|
|
|
|
enum cmd_and_opt_values c = *ret_cmd;
|
|
|
|
|
|
|
|
|
|
if (!c || c == new_cmd)
|
|
|
|
|
c = new_cmd;
|
|
|
|
|
else if (c == aSign && new_cmd == aEncrypt)
|
|
|
|
|
c = aSignEncrypt;
|
|
|
|
|
else if (c == aEncrypt && new_cmd == aSign)
|
|
|
|
|
c = aSignEncrypt;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
else
|
2010-06-07 13:33:02 +00:00
|
|
|
|
{
|
|
|
|
|
log_error (_("conflicting commands\n"));
|
|
|
|
|
exit (2);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-06 17:46:40 +01:00
|
|
|
|
*ret_cmd = c;
|
2010-06-07 13:33:02 +00:00
|
|
|
|
}
|
2019-03-06 17:46:40 +01:00
|
|
|
|
|
2015-11-26 14:08:48 +01:00
|
|
|
|
|
2019-03-06 17:46:40 +01:00
|
|
|
|
|
2015-11-26 14:08:48 +01:00
|
|
|
|
/* Shell-like argument splitting.
|
|
|
|
|
|
|
|
|
|
For compatibility with gpg-zip we accept arguments for GnuPG and
|
|
|
|
|
tar given as a string argument to '--gpg-args' and '--tar-args'.
|
|
|
|
|
gpg-zip was implemented as a Bourne Shell script, and therefore, we
|
|
|
|
|
need to split the string the same way the shell would. */
|
|
|
|
|
static int
|
|
|
|
|
shell_parse_stringlist (const char *str, strlist_t *r_list)
|
|
|
|
|
{
|
|
|
|
|
strlist_t list = NULL;
|
|
|
|
|
const char *s = str;
|
|
|
|
|
char quoted = 0;
|
|
|
|
|
char arg[1024];
|
|
|
|
|
char *p = arg;
|
|
|
|
|
#define addchar(c) \
|
|
|
|
|
do { if (p - arg + 2 < sizeof arg) *p++ = (c); else return 1; } while (0)
|
|
|
|
|
#define addargument() \
|
|
|
|
|
do { \
|
|
|
|
|
if (p > arg) \
|
|
|
|
|
{ \
|
|
|
|
|
*p = 0; \
|
|
|
|
|
append_to_strlist (&list, arg); \
|
|
|
|
|
p = arg; \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
#define unquoted 0
|
|
|
|
|
#define singlequote '\''
|
|
|
|
|
#define doublequote '"'
|
|
|
|
|
|
|
|
|
|
for (; *s; s++)
|
|
|
|
|
{
|
|
|
|
|
switch (quoted)
|
|
|
|
|
{
|
|
|
|
|
case unquoted:
|
|
|
|
|
if (isspace (*s))
|
|
|
|
|
addargument ();
|
|
|
|
|
else if (*s == singlequote || *s == doublequote)
|
|
|
|
|
quoted = *s;
|
|
|
|
|
else
|
|
|
|
|
addchar (*s);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case singlequote:
|
|
|
|
|
if (*s == singlequote)
|
|
|
|
|
quoted = unquoted;
|
|
|
|
|
else
|
|
|
|
|
addchar (*s);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case doublequote:
|
2022-03-04 14:54:17 +01:00
|
|
|
|
log_assert (s > str || !"cannot be quoted at first char");
|
2015-11-26 14:08:48 +01:00
|
|
|
|
if (*s == doublequote && *(s - 1) != '\\')
|
|
|
|
|
quoted = unquoted;
|
|
|
|
|
else
|
|
|
|
|
addchar (*s);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2022-03-04 14:54:17 +01:00
|
|
|
|
log_assert (! "reached");
|
2015-11-26 14:08:48 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Append the last argument. */
|
|
|
|
|
addargument ();
|
|
|
|
|
|
|
|
|
|
#undef doublequote
|
|
|
|
|
#undef singlequote
|
|
|
|
|
#undef unquoted
|
|
|
|
|
#undef addargument
|
|
|
|
|
#undef addchar
|
|
|
|
|
*r_list = list;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Like shell_parse_stringlist, but returns an argv vector
|
|
|
|
|
instead of a strlist. */
|
|
|
|
|
static int
|
|
|
|
|
shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
strlist_t list;
|
|
|
|
|
|
|
|
|
|
if (shell_parse_stringlist (s, &list))
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
*r_argc = strlist_length (list);
|
|
|
|
|
*r_argv = xtrycalloc (*r_argc, sizeof **r_argv);
|
|
|
|
|
if (*r_argv == NULL)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
for (i = 0; list; i++)
|
2016-07-01 13:44:32 +02:00
|
|
|
|
{
|
|
|
|
|
gpgrt_annotate_leaked_object (list);
|
|
|
|
|
(*r_argv)[i] = list->d;
|
|
|
|
|
list = list->next;
|
|
|
|
|
}
|
|
|
|
|
gpgrt_annotate_leaked_object (*r_argv);
|
2015-11-26 14:08:48 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-06-07 13:33:02 +00:00
|
|
|
|
|
|
|
|
|
|
2019-03-06 17:46:40 +01:00
|
|
|
|
|
2015-11-26 11:46:35 +01:00
|
|
|
|
/* Command line parsing. */
|
|
|
|
|
static void
|
2015-11-26 14:08:48 +01:00
|
|
|
|
parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
|
2015-11-26 11:46:35 +01:00
|
|
|
|
{
|
|
|
|
|
int no_more_options = 0;
|
2010-06-07 13:33:02 +00:00
|
|
|
|
|
2020-12-04 11:51:48 +01:00
|
|
|
|
while (!no_more_options && gnupg_argparse (NULL, pargs, popts))
|
2010-06-07 13:33:02 +00:00
|
|
|
|
{
|
2015-11-26 11:46:35 +01:00
|
|
|
|
switch (pargs->r_opt)
|
2010-06-07 13:33:02 +00:00
|
|
|
|
{
|
2015-11-26 11:46:35 +01:00
|
|
|
|
case oOutput: opt.outfile = pargs->r.ret_str; break;
|
|
|
|
|
case oDirectory: opt.directory = pargs->r.ret_str; break;
|
|
|
|
|
case oSetFilename: opt.filename = pargs->r.ret_str; break;
|
2010-06-07 13:33:02 +00:00
|
|
|
|
case oQuiet: opt.quiet = 1; break;
|
|
|
|
|
case oVerbose: opt.verbose++; break;
|
|
|
|
|
case oNoVerbose: opt.verbose = 0; break;
|
2015-11-26 11:46:35 +01:00
|
|
|
|
case oFilesFrom: files_from = pargs->r.ret_str; break;
|
2010-08-09 09:05:18 +00:00
|
|
|
|
case oNull: null_names = 1; break;
|
2020-08-21 21:28:28 +02:00
|
|
|
|
case oUtf8Strings: opt.utf8strings = 1; break;
|
2023-06-15 13:52:18 +02:00
|
|
|
|
case oNoCompress: opt.no_compress = 1; break;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
case aList:
|
|
|
|
|
case aDecrypt:
|
|
|
|
|
case aEncrypt:
|
|
|
|
|
case aSign:
|
2015-11-26 11:46:35 +01:00
|
|
|
|
set_cmd (&cmd, pargs->r_opt);
|
2010-06-07 13:33:02 +00:00
|
|
|
|
break;
|
|
|
|
|
|
2016-07-05 14:29:29 +02:00
|
|
|
|
case aCreate:
|
|
|
|
|
set_cmd (&cmd, aEncrypt);
|
|
|
|
|
skip_crypto = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case aExtract:
|
|
|
|
|
set_cmd (&cmd, aDecrypt);
|
|
|
|
|
skip_crypto = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2015-11-24 18:39:30 +01:00
|
|
|
|
case oRecipient:
|
2015-11-26 11:46:35 +01:00
|
|
|
|
add_to_strlist (&opt.recipients, pargs->r.ret_str);
|
2015-11-24 18:39:30 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case oUser:
|
2015-11-26 11:46:35 +01:00
|
|
|
|
opt.user = pargs->r.ret_str;
|
2015-11-24 18:39:30 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
case oSymmetric:
|
|
|
|
|
set_cmd (&cmd, aEncrypt);
|
|
|
|
|
opt.symmetric = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2015-11-25 14:57:14 +01:00
|
|
|
|
case oGpgProgram:
|
2015-11-26 11:46:35 +01:00
|
|
|
|
opt.gpg_program = pargs->r.ret_str;
|
2015-11-25 14:57:14 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
case oSkipCrypto:
|
|
|
|
|
skip_crypto = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2010-08-09 09:36:49 +00:00
|
|
|
|
case oOpenPGP: /* Dummy option for now. */ break;
|
|
|
|
|
case oCMS: /* Dummy option for now. */ break;
|
|
|
|
|
|
2022-03-21 13:06:00 +01:00
|
|
|
|
case oBatch: opt.batch = 1; break;
|
|
|
|
|
case oAnswerYes: opt.answer_yes = 1; break;
|
|
|
|
|
case oAnswerNo: opt.answer_no = 1; break;
|
|
|
|
|
case oStatusFD: opt.status_fd = pargs->r.ret_int; break;
|
|
|
|
|
case oRequireCompliance: opt.require_compliance = 1; break;
|
2022-03-22 10:19:55 +01:00
|
|
|
|
case oWithLog: opt.with_log = 1; break;
|
2022-03-21 13:06:00 +01:00
|
|
|
|
|
2015-11-26 15:36:52 +01:00
|
|
|
|
case oGpgArgs:;
|
2015-11-27 17:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
strlist_t list;
|
|
|
|
|
if (shell_parse_stringlist (pargs->r.ret_str, &list))
|
|
|
|
|
log_error ("failed to parse gpg arguments '%s'\n",
|
|
|
|
|
pargs->r.ret_str);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (opt.gpg_arguments)
|
|
|
|
|
strlist_last (opt.gpg_arguments)->next = list;
|
|
|
|
|
else
|
|
|
|
|
opt.gpg_arguments = list;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-26 15:36:52 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2020-12-04 11:51:48 +01:00
|
|
|
|
case oTarArgs:
|
2015-11-27 17:12:56 +01:00
|
|
|
|
{
|
|
|
|
|
int tar_argc;
|
|
|
|
|
char **tar_argv;
|
|
|
|
|
|
|
|
|
|
if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
|
|
|
|
|
log_error ("failed to parse tar arguments '%s'\n",
|
|
|
|
|
pargs->r.ret_str);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ARGPARSE_ARGS tar_args;
|
|
|
|
|
tar_args.argc = &tar_argc;
|
|
|
|
|
tar_args.argv = &tar_argv;
|
|
|
|
|
tar_args.flags = ARGPARSE_FLAG_ARG0;
|
|
|
|
|
parse_arguments (&tar_args, tar_opts);
|
2020-12-04 11:51:48 +01:00
|
|
|
|
gnupg_argparse (NULL, &tar_args, NULL);
|
2015-11-27 17:12:56 +01:00
|
|
|
|
if (tar_args.err)
|
|
|
|
|
log_error ("unsupported tar arguments '%s'\n",
|
|
|
|
|
pargs->r.ret_str);
|
|
|
|
|
pargs->err = tar_args.err;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-26 14:08:48 +01:00
|
|
|
|
break;
|
|
|
|
|
|
2015-11-26 17:05:12 +01:00
|
|
|
|
case oDryRun:
|
|
|
|
|
opt.dry_run = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
2015-11-26 11:46:35 +01:00
|
|
|
|
default: pargs->err = 2; break;
|
2010-06-07 13:33:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-11-26 11:46:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* gpgtar main. */
|
|
|
|
|
int
|
|
|
|
|
main (int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *fname;
|
|
|
|
|
ARGPARSE_ARGS pargs;
|
|
|
|
|
|
|
|
|
|
gnupg_reopen_std (GPGTAR_NAME);
|
|
|
|
|
set_strusage (my_strusage);
|
2023-03-15 12:05:45 +01:00
|
|
|
|
log_set_prefix (GPGTAR_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY);
|
2015-11-26 11:46:35 +01:00
|
|
|
|
|
|
|
|
|
/* Make sure that our subsystems are ready. */
|
|
|
|
|
i18n_init();
|
|
|
|
|
init_common_subsystems (&argc, &argv);
|
2022-03-21 13:06:00 +01:00
|
|
|
|
gnupg_init_signals (0, NULL);
|
2015-11-26 11:46:35 +01:00
|
|
|
|
|
2020-12-04 11:51:48 +01:00
|
|
|
|
log_assert (sizeof (struct ustar_raw_header) == 512);
|
|
|
|
|
|
2022-03-21 13:06:00 +01:00
|
|
|
|
/* Set default options */
|
|
|
|
|
opt.status_fd = -1;
|
|
|
|
|
|
2015-11-26 11:46:35 +01:00
|
|
|
|
/* Parse the command line. */
|
|
|
|
|
pargs.argc = &argc;
|
|
|
|
|
pargs.argv = &argv;
|
|
|
|
|
pargs.flags = ARGPARSE_FLAG_KEEP;
|
2015-11-26 14:08:48 +01:00
|
|
|
|
parse_arguments (&pargs, opts);
|
2020-12-04 11:51:48 +01:00
|
|
|
|
gnupg_argparse (NULL, &pargs, NULL);
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
if (log_get_errorcount (0))
|
|
|
|
|
exit (2);
|
|
|
|
|
|
2012-03-27 12:35:13 +02:00
|
|
|
|
/* Print a warning if an argument looks like an option. */
|
|
|
|
|
if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i=0; i < argc; i++)
|
|
|
|
|
if (argv[i][0] == '-' && argv[i][1] == '-')
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
|
2012-03-27 12:35:13 +02:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-30 15:23:38 +01:00
|
|
|
|
/* Set status stream for our own use of --status-fd. The original
|
|
|
|
|
* status fd is passed verbatim to gpg. */
|
2023-03-15 11:18:29 +01:00
|
|
|
|
if (opt.status_fd != -1)
|
2023-01-30 15:23:38 +01:00
|
|
|
|
{
|
|
|
|
|
int fd = translate_sys2libc_fd_int (opt.status_fd, 1);
|
|
|
|
|
|
|
|
|
|
if (!gnupg_fd_valid (fd))
|
|
|
|
|
log_fatal ("status-fd is invalid: %s\n", strerror (errno));
|
|
|
|
|
|
|
|
|
|
if (fd == 1)
|
2023-03-15 12:05:45 +01:00
|
|
|
|
{
|
|
|
|
|
opt.status_stream = es_stdout;
|
|
|
|
|
if (!skip_crypto)
|
|
|
|
|
log_fatal ("using stdout for the status-fd is not possible\n");
|
|
|
|
|
}
|
2023-01-30 15:23:38 +01:00
|
|
|
|
else if (fd == 2)
|
|
|
|
|
opt.status_stream = es_stderr;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
opt.status_stream = es_fdopen (fd, "w");
|
|
|
|
|
if (opt.status_stream)
|
|
|
|
|
es_setvbuf (opt.status_stream, NULL, _IOLBF, 0);
|
|
|
|
|
}
|
|
|
|
|
if (!opt.status_stream)
|
|
|
|
|
{
|
|
|
|
|
log_fatal ("can't open fd %d for status output: %s\n",
|
|
|
|
|
fd, strerror (errno));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-30 16:21:22 +01:00
|
|
|
|
if (! opt.gpg_program)
|
|
|
|
|
opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
|
|
|
|
|
|
2015-11-24 18:39:30 +01:00
|
|
|
|
if (opt.verbose > 1)
|
|
|
|
|
opt.debug_level = 1024;
|
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
switch (cmd)
|
|
|
|
|
{
|
2023-01-26 11:39:19 +01:00
|
|
|
|
case aDecrypt:
|
2010-06-07 13:33:02 +00:00
|
|
|
|
case aList:
|
|
|
|
|
if (argc > 1)
|
|
|
|
|
usage (1);
|
2023-01-26 11:39:19 +01:00
|
|
|
|
fname = (argc && strcmp (*argv, "-"))? *argv : NULL;
|
2010-07-16 13:19:45 +00:00
|
|
|
|
if (opt.filename)
|
|
|
|
|
log_info ("note: ignoring option --set-filename\n");
|
2010-08-09 09:05:18 +00:00
|
|
|
|
if (files_from)
|
|
|
|
|
log_info ("note: ignoring option --files-from\n");
|
2023-01-26 11:39:19 +01:00
|
|
|
|
if (cmd == aDecrypt)
|
|
|
|
|
{
|
|
|
|
|
err = gpgtar_extract (fname, !skip_crypto);
|
|
|
|
|
if (err && !log_get_errorcount (0))
|
|
|
|
|
log_error ("extracting archive failed: %s\n", gpg_strerror (err));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
err = gpgtar_list (fname, !skip_crypto);
|
|
|
|
|
if (err && !log_get_errorcount (0))
|
|
|
|
|
log_error ("listing archive failed: %s\n", gpg_strerror (err));
|
|
|
|
|
}
|
2010-06-07 13:33:02 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case aEncrypt:
|
2015-11-30 18:39:00 +01:00
|
|
|
|
case aSign:
|
|
|
|
|
case aSignEncrypt:
|
2020-08-20 15:50:50 +02:00
|
|
|
|
if ((!argc && !files_from)
|
|
|
|
|
|| (argc && files_from))
|
2010-06-07 13:33:02 +00:00
|
|
|
|
usage (1);
|
2010-07-16 13:19:45 +00:00
|
|
|
|
if (opt.filename)
|
|
|
|
|
log_info ("note: ignoring option --set-filename\n");
|
2020-08-20 15:50:50 +02:00
|
|
|
|
err = gpgtar_create (files_from? NULL : argv,
|
|
|
|
|
files_from,
|
|
|
|
|
null_names,
|
2015-11-30 18:39:00 +01:00
|
|
|
|
!skip_crypto
|
|
|
|
|
&& (cmd == aEncrypt || cmd == aSignEncrypt),
|
|
|
|
|
cmd == aSign || cmd == aSignEncrypt);
|
2015-11-25 13:39:50 +01:00
|
|
|
|
if (err && log_get_errorcount (0) == 0)
|
|
|
|
|
log_error ("creating archive failed: %s\n", gpg_strerror (err));
|
2010-06-07 13:33:02 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
log_error (_("invalid command (there is no implicit command)\n"));
|
2023-05-19 13:06:18 +02:00
|
|
|
|
err = 0;
|
2010-06-07 13:33:02 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-19 13:06:18 +02:00
|
|
|
|
if (opt.status_stream)
|
|
|
|
|
{
|
|
|
|
|
if (err || log_get_errorcount (0))
|
|
|
|
|
es_fprintf (opt.status_stream, "[GNUPG:] FAILURE - %u\n",
|
|
|
|
|
err? err : gpg_error (GPG_ERR_GENERAL));
|
|
|
|
|
else
|
|
|
|
|
es_fprintf (opt.status_stream, "[GNUPG:] SUCCESS\n");
|
|
|
|
|
}
|
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
return log_get_errorcount (0)? 1:0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read the next record from STREAM. RECORD is a buffer provided by
|
2022-03-21 13:06:00 +01:00
|
|
|
|
the caller and must be at least of size RECORDSIZE. The function
|
2017-02-20 16:19:50 -05:00
|
|
|
|
return 0 on success and error code on failure; a diagnostic
|
2010-06-07 13:33:02 +00:00
|
|
|
|
printed as well. Note that there is no need for an EOF indicator
|
|
|
|
|
because a tarball has an explicit EOF record. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
read_record (estream_t stream, void *record)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
size_t nread;
|
|
|
|
|
|
|
|
|
|
nread = es_fread (record, 1, RECORDSIZE, stream);
|
|
|
|
|
if (nread != RECORDSIZE)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
if (es_ferror (stream))
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("error reading '%s': %s\n",
|
2010-06-07 13:33:02 +00:00
|
|
|
|
es_fname_get (stream), gpg_strerror (err));
|
|
|
|
|
else
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("error reading '%s': premature EOF "
|
2010-06-07 13:33:02 +00:00
|
|
|
|
"(size of last record: %zu)\n",
|
|
|
|
|
es_fname_get (stream), nread);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
|
|
|
|
|
name of the file used for diagnostics. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
write_record (estream_t stream, const void *record)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
size_t nwritten;
|
|
|
|
|
|
|
|
|
|
nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
|
|
|
|
|
if (nwritten != RECORDSIZE)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("error writing '%s': %s\n",
|
2010-06-07 13:33:02 +00:00
|
|
|
|
es_fname_get (stream), gpg_strerror (err));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
err = 0;
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return true if FP is an unarmored OpenPGP message. Note that this
|
2015-11-16 12:41:46 +01:00
|
|
|
|
function reads a few bytes from FP but pushes them back. */
|
2010-07-16 13:19:45 +00:00
|
|
|
|
#if 0
|
2010-06-07 13:33:02 +00:00
|
|
|
|
static int
|
|
|
|
|
openpgp_message_p (estream_t fp)
|
|
|
|
|
{
|
|
|
|
|
int ctb;
|
|
|
|
|
|
|
|
|
|
ctb = es_getc (fp);
|
|
|
|
|
if (ctb != EOF)
|
|
|
|
|
{
|
|
|
|
|
if (es_ungetc (ctb, fp))
|
2012-02-06 20:50:47 +01:00
|
|
|
|
log_fatal ("error ungetting first byte: %s\n",
|
2010-06-07 13:33:02 +00:00
|
|
|
|
gpg_strerror (gpg_error_from_syserror ()));
|
2012-02-06 20:50:47 +01:00
|
|
|
|
|
2010-06-07 13:33:02 +00:00
|
|
|
|
if ((ctb & 0x80))
|
|
|
|
|
{
|
|
|
|
|
switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
|
|
|
|
|
{
|
|
|
|
|
case PKT_MARKER:
|
|
|
|
|
case PKT_SYMKEY_ENC:
|
|
|
|
|
case PKT_ONEPASS_SIG:
|
|
|
|
|
case PKT_PUBKEY_ENC:
|
|
|
|
|
case PKT_SIGNATURE:
|
|
|
|
|
case PKT_COMMENT:
|
|
|
|
|
case PKT_OLD_COMMENT:
|
|
|
|
|
case PKT_PLAINTEXT:
|
|
|
|
|
case PKT_COMPRESSED:
|
|
|
|
|
case PKT_ENCRYPTED:
|
|
|
|
|
return 1; /* Yes, this seems to be an OpenPGP message. */
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-07-16 13:19:45 +00:00
|
|
|
|
#endif
|