Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34166169
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
35 KB
Subscribers
None
View Options
diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c
index a83d97495..4d2d6d46b 100644
--- a/g10/pubkey-enc.c
+++ b/g10/pubkey-enc.c
@@ -1,573 +1,573 @@
/* pubkey-enc.c - Process a public key encoded packet.
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
* 2010 Free Software Foundation, Inc.
*
* 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
* along with this program; if not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "gpg.h"
#include "../common/util.h"
#include "packet.h"
#include "keydb.h"
#include "trustdb.h"
#include "../common/status.h"
#include "options.h"
#include "main.h"
#include "../common/i18n.h"
#include "pkglue.h"
#include "call-agent.h"
#include "../common/host2net.h"
#include "../common/compliance.h"
static gpg_error_t get_it (ctrl_t ctrl, struct pubkey_enc_list *k,
DEK *dek, PKT_public_key *sk, u32 *keyid);
/* Check that the given algo is mentioned in one of the valid user-ids. */
static int
is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo)
{
kbnode_t k;
for (k = keyblock; k; k = k->next)
{
if (k->pkt->pkttype == PKT_USER_ID)
{
PKT_user_id *uid = k->pkt->pkt.user_id;
prefitem_t *prefs = uid->prefs;
if (uid->created && prefs && !uid->flags.revoked && !uid->flags.expired)
{
for (; prefs->type; prefs++)
if (prefs->type == type && prefs->value == algo)
return 1;
}
}
}
return 0;
}
/*
* Get the session key from a pubkey enc packet and return it in DEK,
* which should have been allocated in secure memory by the caller.
*/
gpg_error_t
get_session_key (ctrl_t ctrl, struct pubkey_enc_list *list, DEK *dek)
{
PKT_public_key *sk = NULL;
gpg_error_t err;
void *enum_context = NULL;
u32 keyid[2];
int search_for_secret_keys = 1;
struct pubkey_enc_list *k;
if (DBG_CLOCK)
log_clock ("get_session_key enter");
while (search_for_secret_keys)
{
sk = xmalloc_clear (sizeof *sk);
err = enum_secret_keys (ctrl, &enum_context, sk);
if (err)
break;
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION,
sk->pubkey_algo, 0,
sk->pkey, nbits_from_pk (sk), NULL))
{
log_info (_("key %s is not suitable for decryption"
" in %s mode\n"),
keystr_from_pk (sk),
gnupg_compliance_option_string (opt.compliance));
continue;
}
/* FIXME: The list needs to be sorted so that we try the keys in
* an appropriate order. For example:
* - On-disk keys w/o protection
* - On-disk keys with a cached passphrase
* - On-card keys of an active card
* - On-disk keys with protection
* - On-card keys from cards which are not plugged it. Here a
* cancel-all button should stop asking for other cards.
* Without any anonymous keys the sorting can be skipped.
*/
for (k = list; k; k = k->next)
{
if (!(k->d.pubkey_algo == PUBKEY_ALGO_ELGAMAL_E
|| k->d.pubkey_algo == PUBKEY_ALGO_ECDH
|| k->d.pubkey_algo == PUBKEY_ALGO_KYBER
|| k->d.pubkey_algo == PUBKEY_ALGO_RSA
|| k->d.pubkey_algo == PUBKEY_ALGO_RSA_E
|| k->d.pubkey_algo == PUBKEY_ALGO_ELGAMAL))
continue;
if (openpgp_pk_test_algo2 (k->d.pubkey_algo, PUBKEY_USAGE_ENC))
continue;
if (sk->pubkey_algo != k->d.pubkey_algo)
continue;
keyid_from_pk (sk, keyid);
if (!k->d.keyid[0] && !k->d.keyid[1])
{
if (opt.skip_hidden_recipients)
continue;
if (!opt.quiet)
log_info (_("anonymous recipient; trying secret key %s ...\n"),
keystr (keyid));
}
else if (opt.try_all_secrets
|| (k->d.keyid[0] == keyid[0] && k->d.keyid[1] == keyid[1]))
{
if (!opt.quiet && !(sk->pubkey_usage & PUBKEY_USAGE_XENC_MASK))
log_info (_("used key is not marked for encryption use.\n"));
}
else
continue;
err = get_it (ctrl, k, dek, sk, keyid);
k->result = err;
if (!err)
{
if (!opt.quiet && !k->d.keyid[0] && !k->d.keyid[1])
{
log_info (_("okay, we are the anonymous recipient.\n"));
if (!(sk->pubkey_usage & PUBKEY_USAGE_XENC_MASK))
log_info (_("used key is not marked for encryption use.\n")
);
}
search_for_secret_keys = 0;
break;
}
else if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
{
search_for_secret_keys = 0;
break; /* Don't try any more secret keys. */
}
}
}
enum_secret_keys (ctrl, &enum_context, NULL); /* free context */
if (gpg_err_code (err) == GPG_ERR_EOF)
{
err = gpg_error (GPG_ERR_NO_SECKEY);
/* Return the last specific error, if any. */
for (k = list; k; k = k->next)
if (k->result != -1)
err = k->result;
}
if (DBG_CLOCK)
log_clock ("get_session_key leave");
return err;
}
static gpg_error_t
get_it (ctrl_t ctrl,
struct pubkey_enc_list *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
{
gpg_error_t err;
byte *frame = NULL;
unsigned int frameidx;
size_t nframe;
u16 csum, csum2;
int padding;
gcry_sexp_t s_data;
char *desc;
char *keygrip;
byte fp[MAX_FINGERPRINT_LEN];
if (DBG_CLOCK)
log_clock ("decryption start");
/* Get the keygrip. */
err = hexkeygrip_from_pk (sk, &keygrip);
if (err)
goto leave;
/* Convert the data to an S-expression. */
if (sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL
|| sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E)
{
if (!enc->d.data[0] || !enc->d.data[1])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
enc->d.data[0], enc->d.data[1]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_RSA
|| sk->pubkey_algo == PUBKEY_ALGO_RSA_E)
{
if (!enc->d.data[0])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
enc->d.data[0]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
if (!enc->d.data[0] || !enc->d.data[1])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(s%m)(e%m)))",
enc->d.data[1], enc->d.data[0]);
}
else if (sk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
char fixedinfo[1+MAX_FINGERPRINT_LEN];
int fixedlen;
if ((opt.compat_flags & COMPAT_T7014_OLD))
{
/* Temporary use for tests with original test vectors. */
fixedinfo[0] = 0x69;
fixedlen = 1;
}
else
{
fixedinfo[0] = enc->d.seskey_algo;
v5_fingerprint_from_pk (sk, fixedinfo+1, NULL);
fixedlen = 33;
}
if (!enc->d.data[0] || !enc->d.data[1] || !enc->d.data[2])
err = gpg_error (GPG_ERR_BAD_MPI);
else
err = gcry_sexp_build (&s_data, NULL,
"(enc-val(pqc(e%m)(k%m)(s%m)(c%d)(fixed-info%b)))",
enc->d.data[0], enc->d.data[1], enc->d.data[2],
enc->d.seskey_algo, fixedlen, fixedinfo);
}
else
err = gpg_error (GPG_ERR_BUG);
if (err)
goto leave;
if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
fingerprint_from_pk (sk, fp, NULL);
/* Decrypt. */
desc = gpg_format_keydesc (ctrl, sk, FORMAT_KEYDESC_NORMAL, 1);
err = agent_pkdecrypt (NULL, keygrip,
desc, sk->keyid, sk->main_keyid, sk->pubkey_algo,
s_data, &frame, &nframe, &padding);
xfree (desc);
gcry_sexp_release (s_data);
if (err)
goto leave;
/* Now get the DEK (data encryption key) from the frame
*
* Old versions encode the DEK in this format (msb is left):
*
* 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
*
* Later versions encode the DEK like this:
*
* 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
*
* (mpi_get_buffer already removed the leading zero).
*
* RND are non-zero randow bytes.
* A is the cipher algorithm
* DEK is the encryption key (session key) with length k
* CSUM
*/
if (DBG_CRYPTO)
log_printhex (frame, nframe, "DEK frame:");
frameidx = 0;
if (sk->pubkey_algo == PUBKEY_ALGO_KYBER)
{
/* We expect a 32 byte session key. We should not see this
* error here because due to the KEM mode the agent_pkdecrypt
* should have already failed. */
if (nframe != 32)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
dek->keylen = nframe;
dek->algo = enc->d.seskey_algo;
}
else if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
gcry_mpi_t decoded;
/* At the beginning the frame are the bytes of shared point MPI. */
err = pk_ecdh_decrypt (&decoded, fp,
enc->d.data[1], /*encr data as an MPI*/
frame, nframe,
sk->pkey);
if(err)
goto leave;
xfree (frame);
err = gcry_mpi_aprint (GCRYMPI_FMT_USG, &frame, &nframe, decoded);
mpi_release (decoded);
if (err)
goto leave;
/* Now the frame are the bytes decrypted but padded session key. */
if (!nframe || nframe <= 8
|| frame[nframe-1] > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
nframe -= frame[nframe-1]; /* Remove padding. */
if (4 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
dek->keylen = nframe - 3;
dek->algo = frame[0];
frameidx = 1;
}
else
{
if (padding)
{
if (7 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* FIXME: Actually the leading zero is required but due to
* the way we encode the output in libgcrypt as an MPI we
* are not able to encode that leading zero. However, when
* using a Smartcard we are doing it the right way and
* therefore we have to skip the zero. This should be fixed
* in gpg-agent of course. */
frameidx = 0;
if (!frame[frameidx])
frameidx++;
if (frame[frameidx] == 1 && frame[nframe - 1] == 2)
{
log_info (_("old encoding of the DEK is not supported\n"));
err = gpg_error (GPG_ERR_CIPHER_ALGO);
goto leave;
}
if (frame[frameidx] != 2) /* Something went wrong. */
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* Skip the random bytes. */
for (frameidx++; frameidx < nframe && frame[frameidx]; frameidx++)
;
frameidx++; /* Skip the zero byte. */
}
if (frameidx + 4 > nframe)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
dek->keylen = nframe - (frameidx + 1) - 2;
dek->algo = frame[frameidx++];
}
/* Check whether we support the ago. */
err = openpgp_cipher_test_algo (dek->algo);
if (err)
{
if (!opt.quiet && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO)
{
log_info (_("cipher algorithm %d%s is unknown or disabled\n"),
dek->algo,
dek->algo == CIPHER_ALGO_IDEA ? " (IDEA)" : "");
}
dek->algo = 0;
goto leave;
}
if (dek->keylen != openpgp_cipher_get_algo_keylen (dek->algo))
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
/* Copy the key to DEK and compare the checksum if needed. */
/* We use the frameidx as flag for the need of a checksum. */
memcpy (dek->key, frame + frameidx, dek->keylen);
if (frameidx)
{
csum = buf16_to_u16 (frame+nframe-2);
for (csum2 = 0, frameidx = 0; frameidx < dek->keylen; frameidx++)
csum2 += dek->key[frameidx];
if (csum != csum2)
{
err = gpg_error (GPG_ERR_WRONG_SECKEY);
goto leave;
}
}
if (DBG_CLOCK)
log_clock ("decryption ready");
if (DBG_CRYPTO)
log_printhex (dek->key, dek->keylen, "DEK is:");
/* Check that the algo is in the preferences and whether it has
* expired. Also print a status line with the key's fingerprint. */
{
PKT_public_key *pk = NULL;
PKT_public_key *mainpk = NULL;
KBNODE pkb = get_pubkeyblock_ext (ctrl, keyid, GET_PUBKEYBLOCK_FLAG_ADSK);
if (!pkb)
{
- err = -1;
- log_error ("oops: public key not found for preference check\n");
+ err = gpg_error (GPG_ERR_UNEXPECTED);
+ log_info ("oops: public key not found for preference check\n");
}
else if (pkb->pkt->pkt.public_key->selfsigversion > 3
&& dek->algo != CIPHER_ALGO_3DES
&& !opt.quiet
&& !is_algo_in_prefs (pkb, PREFTYPE_SYM, dek->algo))
log_info (_("WARNING: cipher algorithm %s not found in recipient"
" preferences\n"), openpgp_cipher_algo_name (dek->algo));
/* if (!err && 25519 && openpgp_oidbuf_is_ed25519 (curve, len)) */
/* log_info ("Note: legacy OID was used for cv25519\n"); */
if (!err)
{
kbnode_t k;
int first = 1;
for (k = pkb; k; k = k->next)
{
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
{
u32 aki[2];
if (first)
{
first = 0;
mainpk = k->pkt->pkt.public_key;
}
keyid_from_pk (k->pkt->pkt.public_key, aki);
if (aki[0] == keyid[0] && aki[1] == keyid[1])
{
pk = k->pkt->pkt.public_key;
break;
}
}
}
if (!pk)
BUG ();
if (pk->expiredate && pk->expiredate <= make_timestamp ())
{
log_info (_("Note: secret key %s expired at %s\n"),
keystr (keyid), asctimestamp (pk->expiredate));
}
}
if (pk && !(pk->pubkey_usage & PUBKEY_USAGE_ENC)
&& (pk->pubkey_usage & PUBKEY_USAGE_RENC))
{
log_info (_("Note: ADSK key has been used for decryption"));
log_printf ("\n");
}
if (pk && pk->flags.revoked)
{
log_info (_("Note: key has been revoked"));
log_printf ("\n");
show_revocation_reason (ctrl, pk, 1);
}
if (is_status_enabled () && pk && mainpk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
hexfingerprint (pk, pkhex, sizeof pkhex);
hexfingerprint (mainpk, mainpkhex, sizeof mainpkhex);
/* Note that we do not want to create a trustdb just for
* getting the ownertrust: If there is no trustdb there can't
* be an ultimately trusted key anyway and thus the ownertrust
* value is irrelevant. */
write_status_printf (STATUS_DECRYPTION_KEY, "%s %s %c",
pkhex, mainpkhex,
get_ownertrust_info (ctrl, mainpk, 1));
}
release_kbnode (pkb);
err = 0;
}
leave:
xfree (frame);
xfree (keygrip);
return err;
}
/*
* Get the session key from the given string.
* String is supposed to be formatted as this:
* <algo-id>:<even-number-of-hex-digits>
*/
gpg_error_t
get_override_session_key (DEK *dek, const char *string)
{
const char *s;
int i;
if (!string)
return GPG_ERR_BAD_KEY;
dek->algo = atoi (string);
if (dek->algo < 1)
return GPG_ERR_BAD_KEY;
if (!(s = strchr (string, ':')))
return GPG_ERR_BAD_KEY;
s++;
for (i = 0; i < DIM (dek->key) && *s; i++, s += 2)
{
int c = hextobyte (s);
if (c == -1)
return GPG_ERR_BAD_KEY;
dek->key[i] = c;
}
if (*s)
return GPG_ERR_BAD_KEY;
dek->keylen = i;
return 0;
}
diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c
index c6e3b6065..86e9c1812 100644
--- a/tools/gpgtar-extract.c
+++ b/tools/gpgtar-extract.c
@@ -1,556 +1,599 @@
/* gpgtar-extract.c - Extract from a TAR archive
* Copyright (C) 2016-2017, 2019-2022 g10 Code GmbH
* Copyright (C) 2010, 2012, 2013 Werner Koch
* Copyright (C) 2010 Free Software Foundation, Inc.
*
* 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
* along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "../common/i18n.h"
#include <gpg-error.h>
#include "../common/sysutils.h"
#include "../common/ccparray.h"
#include "gpgtar.h"
static gpg_error_t
check_suspicious_name (const char *name, tarinfo_t info)
{
size_t n;
n = strlen (name);
#ifdef HAVE_DOSISH_SYSTEM
if (strchr (name, '\\'))
{
log_error ("filename '%s' contains a backslash - "
"can't extract on this system\n", name);
info->skipped_badname++;
return gpg_error (GPG_ERR_INV_NAME);
}
#endif /*HAVE_DOSISH_SYSTEM*/
if (!n
|| strstr (name, "//")
|| strstr (name, "/../")
|| !strncmp (name, "../", 3)
|| (n >= 3 && !strcmp (name+n-3, "/.." )))
{
log_error ("filename '%s' has suspicious parts - not extracting\n",
name);
info->skipped_suspicious++;
return gpg_error (GPG_ERR_INV_NAME);
}
return 0;
}
+/* This is our version of mkdir -p. DIRECTORY is the full filename of
+ * the directory and PREFIXLEN is the length of an intial directory
+ * part which already exists. If STRIP is set filename is removed.
+ * If VERBOSE is set a diagnostic is printed to show the created
+ * directory. */
+static gpg_error_t
+try_mkdir_p (const char *directory, size_t prefixlen, int strip, int verbose)
+{
+ gpg_error_t err = 0;
+ char *fname;
+ char *p;
+
+ fname = xtrystrdup (directory);
+ if (!fname)
+ return gpg_error_from_syserror ();
+
+ if (strip) /* Strip last file name. */
+ {
+ p = strrchr (fname, '/');
+ if (p)
+ *p = 0;
+ }
+ else /* Remove a possible trailing slash. */
+ {
+ if (fname[strlen (fname)-1] == '/')
+ fname[strlen (fname)-1] = 0;
+ }
+
+ if (prefixlen >= strlen (fname))
+ goto leave; /* Nothing to create */
+
+ for (p = fname+prefixlen; (p = strchr (p, '/')); p++)
+ {
+ *p = 0;
+ err = gnupg_mkdir (fname, "-rwx------");
+ if (gpg_err_code (err) == GPG_ERR_EEXIST)
+ err = 0;
+ *p = '/';
+ if (err)
+ goto leave;
+ }
+ err = gnupg_mkdir (fname, "-rwx------");
+ if (gpg_err_code (err) == GPG_ERR_EEXIST)
+ err = 0;
+ if (!err && verbose)
+ log_info ("created '%s/'\n", fname);
+
+ leave:
+ xfree (fname);
+ return err;
+}
+
+
+
static gpg_error_t
extract_regular (estream_t stream, const char *dirname,
tarinfo_t info, tar_header_t hdr, strlist_t exthdr)
{
gpg_error_t err;
char record[RECORDSIZE];
size_t n, nbytes, nwritten;
char *fname_buffer = NULL;
const char *fname;
estream_t outfp = NULL;
strlist_t sl;
fname = hdr->name;
for (sl = exthdr; sl; sl = sl->next)
if (sl->flags == 1)
fname = sl->d;
err = check_suspicious_name (fname, info);
if (err)
goto leave;
fname_buffer = strconcat (dirname, "/", fname, NULL);
if (!fname_buffer)
{
err = gpg_error_from_syserror ();
log_error ("error creating filename: %s\n", gpg_strerror (err));
goto leave;
}
fname = fname_buffer;
-
if (opt.dry_run)
outfp = es_fopen ("/dev/null", "wb");
else
outfp = es_fopen (fname, "wb,sysopen");
if (!outfp)
{
err = gpg_error_from_syserror ();
- log_error ("error creating '%s': %s\n", fname, gpg_strerror (err));
- goto leave;
+ /* On ENOENT, try afain after trying to create the directories. */
+ if (!opt.dry_run && gpg_err_code (GPG_ERR_ENOENT)
+ && !try_mkdir_p (fname, strlen (dirname) + 1, 1, opt.verbose))
+ {
+ outfp = es_fopen (fname, "wb,sysopen");
+ err = outfp? 0 : gpg_error_from_syserror ();
+ }
+ if (err)
+ {
+ log_error ("error creating '%s': %s\n", fname, gpg_strerror (err));
+ goto leave;
+ }
}
for (n=0; n < hdr->nrecords;)
{
err = read_record (stream, record);
if (err)
goto leave;
info->nblocks++;
n++;
if (n < hdr->nrecords || (hdr->size && !(hdr->size % RECORDSIZE)))
nbytes = RECORDSIZE;
else
nbytes = (hdr->size % RECORDSIZE);
nwritten = es_fwrite (record, 1, nbytes, outfp);
if (nwritten != nbytes)
{
err = gpg_error_from_syserror ();
log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
goto leave;
}
}
/* Fixme: Set permissions etc. */
leave:
if (!err)
{
if (opt.verbose)
log_info ("extracted '%s'\n", fname);
info->nextracted++;
}
es_fclose (outfp);
if (err && fname && outfp)
{
if (gnupg_remove (fname))
log_error ("error removing incomplete file '%s': %s\n",
fname, gpg_strerror (gpg_error_from_syserror ()));
}
xfree (fname_buffer);
return err;
}
static gpg_error_t
extract_directory (const char *dirname, tarinfo_t info,
tar_header_t hdr, strlist_t exthdr)
{
gpg_error_t err;
const char *name;
char *fname = NULL;
strlist_t sl;
name = hdr->name;
for (sl = exthdr; sl; sl = sl->next)
if (sl->flags == 1)
name = sl->d;
err = check_suspicious_name (name, info);
if (err)
goto leave;
fname = strconcat (dirname, "/", name, NULL);
if (!fname)
{
err = gpg_error_from_syserror ();
log_error ("error creating filename: %s\n", gpg_strerror (err));
goto leave;
}
/* Remove a possible trailing slash. */
if (fname[strlen (fname)-1] == '/')
fname[strlen (fname)-1] = 0;
- if (! opt.dry_run && gnupg_mkdir (fname, "-rwx------"))
+ if (!opt.dry_run && gnupg_mkdir (fname, "-rwx------"))
{
err = gpg_error_from_syserror ();
+ /* Ignore existing directories while extracting. */
if (gpg_err_code (err) == GPG_ERR_EEXIST)
- {
- /* Ignore existing directories while extracting. */
- err = 0;
- }
-
- if (gpg_err_code (err) == GPG_ERR_ENOENT)
+ err = 0;
+ else if (gpg_err_code (err) == GPG_ERR_ENOENT)
{
/* Try to create the directory with parents but keep the
original error code in case of a failure. */
- int rc = 0;
- char *p;
- size_t prefixlen;
-
- /* (PREFIXLEN is the length of the new directory we use to
- * extract the tarball.) */
- prefixlen = strlen (dirname) + 1;
-
- for (p = fname+prefixlen; (p = strchr (p, '/')); p++)
- {
- *p = 0;
- rc = gnupg_mkdir (fname, "-rwx------");
- if (gpg_err_code (rc) == GPG_ERR_EEXIST)
- rc = 0;
- *p = '/';
- if (rc)
- break;
- }
- if (!rc && !gnupg_mkdir (fname, "-rwx------"))
+ if (!try_mkdir_p (fname, strlen (dirname) + 1, 0, 0))
err = 0;
}
+
if (err)
log_error ("error creating directory '%s': %s\n",
fname, gpg_strerror (err));
}
leave:
if (!err && opt.verbose)
log_info ("created '%s/'\n", fname);
xfree (fname);
return err;
}
static gpg_error_t
extract (estream_t stream, const char *dirname, tarinfo_t info,
tar_header_t hdr, strlist_t exthdr)
{
gpg_error_t err;
size_t n;
if (hdr->typeflag == TF_REGULAR || hdr->typeflag == TF_UNKNOWN)
err = extract_regular (stream, dirname, info, hdr, exthdr);
else if (hdr->typeflag == TF_DIRECTORY)
err = extract_directory (dirname, info, hdr, exthdr);
else
{
char record[RECORDSIZE];
log_info ("unsupported file type %d for '%s' - skipped\n",
(int)hdr->typeflag, hdr->name);
if (hdr->typeflag == TF_SYMLINK)
info->skipped_symlinks++;
else if (hdr->typeflag == TF_HARDLINK)
info->skipped_hardlinks++;
else
info->skipped_other++;
for (err = 0, n=0; !err && n < hdr->nrecords; n++)
{
err = read_record (stream, record);
if (!err)
info->nblocks++;
}
}
return err;
}
/* Create a new directory to be used for extracting the tarball.
Returns the name of the directory which must be freed by the
caller. In case of an error a diagnostic is printed and NULL
returned. */
static char *
create_directory (const char *dirprefix)
{
gpg_error_t err = 0;
char *prefix_buffer = NULL;
char *dirname = NULL;
size_t n;
int idx;
/* Remove common suffixes. */
n = strlen (dirprefix);
if (n > 4 && (!compare_filenames (dirprefix + n - 4, EXTSEP_S GPGEXT_GPG)
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "pgp")
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "asc")
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "pem")
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "p7m")
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "p7e")))
{
prefix_buffer = xtrystrdup (dirprefix);
if (!prefix_buffer)
{
err = gpg_error_from_syserror ();
goto leave;
}
prefix_buffer[n-4] = 0;
dirprefix = prefix_buffer;
}
for (idx=1; idx < 5000; idx++)
{
xfree (dirname);
dirname = xtryasprintf ("%s_%d_", dirprefix, idx);
if (!dirname)
{
err = gpg_error_from_syserror ();
goto leave;
}
if (!gnupg_mkdir (dirname, "-rwx------"))
goto leave; /* Ready. */
if (errno != EEXIST && errno != ENOTDIR)
{
err = gpg_error_from_syserror ();
goto leave;
}
}
err = gpg_error (GPG_ERR_LIMIT_REACHED);
leave:
if (err)
{
log_error ("error creating an extract directory: %s\n",
gpg_strerror (err));
xfree (dirname);
dirname = NULL;
}
xfree (prefix_buffer);
return dirname;
}
gpg_error_t
gpgtar_extract (const char *filename, int decrypt)
{
gpg_error_t err;
estream_t stream = NULL;
tar_header_t header = NULL;
strlist_t extheader = NULL;
const char *dirprefix = NULL;
char *dirname = NULL;
struct tarinfo_s tarinfo_buffer;
tarinfo_t tarinfo = &tarinfo_buffer;
gpgrt_process_t proc;
char *logfilename = NULL;
unsigned long long notextracted;
memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer);
if (opt.directory)
dirname = xtrystrdup (opt.directory);
else
{
if (opt.filename)
{
dirprefix = strrchr (opt.filename, '/');
if (dirprefix)
dirprefix++;
else
dirprefix = opt.filename;
}
else if (filename)
{
dirprefix = strrchr (filename, '/');
if (dirprefix)
dirprefix++;
else
dirprefix = filename;
}
if (!dirprefix || !*dirprefix)
dirprefix = "GPGARCH";
dirname = create_directory (dirprefix);
if (!dirname)
{
err = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
}
if (opt.verbose)
log_info ("extracting to '%s/'\n", dirname);
if (decrypt)
{
strlist_t arg;
ccparray_t ccp;
#ifdef HAVE_W32_SYSTEM
HANDLE except[2] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
#else
int except[2] = { -1, -1 };
#endif
const char **argv;
gpgrt_spawn_actions_t act = NULL;
ccparray_init (&ccp, 0);
if (opt.batch)
ccparray_put (&ccp, "--batch");
if (opt.require_compliance)
ccparray_put (&ccp, "--require-compliance");
if (opt.status_fd)
{
static char tmpbuf[40];
es_syshd_t hd;
snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%s", opt.status_fd);
ccparray_put (&ccp, tmpbuf);
es_syshd (opt.status_stream, &hd);
#ifdef HAVE_W32_SYSTEM
except[0] = hd.u.handle;
#else
except[0] = hd.u.fd;
#endif
}
if (opt.with_log)
{
ccparray_put (&ccp, "--log-file");
logfilename = xstrconcat (dirname, ".log", NULL);
ccparray_put (&ccp, logfilename);
}
ccparray_put (&ccp, "--output");
ccparray_put (&ccp, "-");
ccparray_put (&ccp, "--decrypt");
for (arg = opt.gpg_arguments; arg; arg = arg->next)
ccparray_put (&ccp, arg->d);
if (filename)
{
ccparray_put (&ccp, "--");
ccparray_put (&ccp, filename);
}
ccparray_put (&ccp, NULL);
argv = ccparray_get (&ccp, NULL);
if (!argv)
{
err = gpg_error_from_syserror ();
goto leave;
}
err = gpgrt_spawn_actions_new (&act);
if (err)
{
xfree (argv);
goto leave;
}
#ifdef HAVE_W32_SYSTEM
gpgrt_spawn_actions_set_inherit_handles (act, except);
#else
gpgrt_spawn_actions_set_inherit_fds (act, except);
#endif
err = gpgrt_process_spawn (opt.gpg_program, argv,
((filename ? 0 : GPGRT_PROCESS_STDIN_KEEP)
| GPGRT_PROCESS_STDOUT_PIPE), act, &proc);
gpgrt_spawn_actions_release (act);
xfree (argv);
if (err)
goto leave;
gpgrt_process_get_streams (proc, 0, NULL, &stream, NULL);
es_set_binary (stream);
}
else if (filename)
{
if (!strcmp (filename, "-"))
stream = es_stdin;
else
stream = es_fopen (filename, "rb,sysopen");
if (!stream)
{
err = gpg_error_from_syserror ();
log_error ("error opening '%s': %s\n", filename, gpg_strerror (err));
goto leave;
}
if (stream == es_stdin)
es_set_binary (es_stdin);
}
else
{
stream = es_stdin;
es_set_binary (es_stdin);
}
for (;;)
{
err = gpgtar_read_header (stream, tarinfo, &header, &extheader);
if (err || header == NULL)
goto leave;
err = extract (stream, dirname, tarinfo, header, extheader);
if (err)
goto leave;
free_strlist (extheader);
extheader = NULL;
xfree (header);
header = NULL;
}
if (proc)
{
err = es_fclose (stream);
stream = NULL;
if (err)
log_error ("error closing pipe: %s\n", gpg_strerror (err));
err = gpgrt_process_wait (proc, 1);
if (!err)
{
int exitcode;
gpgrt_process_ctl (proc, GPGRT_PROCESS_GET_EXIT_ID, &exitcode);
if (exitcode)
log_error ("running %s failed (exitcode=%d): %s",
opt.gpg_program, exitcode, gpg_strerror (err));
}
gpgrt_process_release (proc);
proc = NULL;
}
leave:
notextracted = tarinfo->skipped_badname;
notextracted += tarinfo->skipped_suspicious;
notextracted += tarinfo->skipped_symlinks;
notextracted += tarinfo->skipped_hardlinks;
notextracted += tarinfo->skipped_other;
if (opt.status_stream)
es_fprintf (opt.status_stream, "[GNUPG:] GPGTAR_EXTRACT"
" %llu %llu %lu %lu %lu %lu %lu\n",
tarinfo->nextracted,
notextracted,
tarinfo->skipped_badname,
tarinfo->skipped_suspicious,
tarinfo->skipped_symlinks,
tarinfo->skipped_hardlinks,
tarinfo->skipped_other);
if (notextracted && !opt.quiet)
{
log_info ("Number of files not extracted: %llu\n", notextracted);
if (tarinfo->skipped_badname)
log_info (" invalid name: %lu\n", tarinfo->skipped_badname);
if (tarinfo->skipped_suspicious)
log_info (" suspicious name: %lu\n", tarinfo->skipped_suspicious);
if (tarinfo->skipped_symlinks)
log_info (" symlink: %lu\n", tarinfo->skipped_symlinks);
if (tarinfo->skipped_hardlinks)
log_info (" hardlink: %lu\n", tarinfo->skipped_hardlinks);
if (tarinfo->skipped_other)
log_info (" other reason: %lu\n", tarinfo->skipped_other);
}
free_strlist (extheader);
xfree (header);
xfree (dirname);
xfree (logfilename);
if (stream != es_stdin)
es_fclose (stream);
return err;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Dec 12, 11:32 AM (1 d, 22 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e7/95/51bdd81b95faa581f663eaf5a75b
Attached To
rG GnuPG
Event Timeline
Log In to Comment