diff --git a/NEWS b/NEWS
index d2560a665..f38cf9f0d 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
Noteworthy changes in version 2.5.0 (unreleased)
------------------------------------------------
+ Changes also found in 2.4.3:
+
+ * Fix garbled time output in non-English Windows. [T6741]
Changes also found in 2.4.3:
@@ -33,6 +36,9 @@ Noteworthy changes in version 2.5.0 (unreleased)
* dirmngr: New option --ignore-crl-extensions. [T6545]
+ * dirmngr: Support config value "none" to disable the default
+ keyserver. [T6708]
+
* wkd: Use export-clean for gpg-wks-client's --mirror and --create
commands. [rG2c7f7a5a27]
@@ -70,6 +76,7 @@ Noteworthy changes in version 2.5.0 (unreleased)
Release dates of 2.4 versions
-----------------------------
+Version 2.4.4 (unreleased) https://dev.gnupg.org/T6578
Version 2.4.3 (2023-07-04) https://dev.gnupg.org/T6509
Version 2.4.2 (2023-05-30) https://dev.gnupg.org/T6506
Version 2.4.1 (2023-04-28) https://dev.gnupg.org/T6454
diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c
index ba0e41c82..4a999ca9a 100644
--- a/agent/call-pinentry.c
+++ b/agent/call-pinentry.c
@@ -175,6 +175,7 @@ unlock_pinentry (ctrl_t ctrl, gpg_error_t rc)
case GPG_ERR_NO_PASSPHRASE:
case GPG_ERR_BAD_PASSPHRASE:
case GPG_ERR_BAD_PIN:
+ case GPG_ERR_BAD_RESET_CODE:
break;
case GPG_ERR_CORRUPTED_PROTECTION:
@@ -1610,12 +1611,13 @@ agent_askpin (ctrl_t ctrl,
&& (pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
return unlock_pinentry (ctrl, rc);
- if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE)
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
+ || gpg_err_code (rc) == GPG_ERR_BAD_PIN
+ || gpg_err_code (rc) == GPG_ERR_BAD_RESET_CODE)
{
if (pininfo->cb_errtext)
errtext = pininfo->cb_errtext;
- else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
- || gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ else
errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase"));
}
else if (rc)
@@ -1883,12 +1885,13 @@ agent_get_passphrase (ctrl_t ctrl,
if (rc && (pininfo->status & PINENTRY_STATUS_PASSWORD_FROM_CACHE))
return unlock_pinentry (ctrl, rc);
- if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE)
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
+ || gpg_err_code (rc) == GPG_ERR_BAD_PIN
+ || gpg_err_code (rc) == GPG_ERR_BAD_RESET_CODE)
{
if (pininfo->cb_errtext)
errtext = pininfo->cb_errtext;
- else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
- || gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ else
errtext = (is_pin? L_("Bad PIN") : L_("Bad Passphrase"));
}
else if (rc)
diff --git a/build-aux/speedo.mk b/build-aux/speedo.mk
index 170c20b79..7777411e2 100644
--- a/build-aux/speedo.mk
+++ b/build-aux/speedo.mk
@@ -282,6 +282,7 @@ AUTHENTICODE_FILES= \
gpgtar.exe \
gpgv.exe \
gpg-card.exe \
+ keyboxd.exe \
libassuan-0.dll \
libgcrypt-20.dll \
libgpg-error-0.dll \
diff --git a/common/Makefile.am b/common/Makefile.am
index 189874ab5..0829f5113 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -58,7 +58,7 @@ common_sources = \
openpgpdefs.h \
gc-opt-flags.h \
sexp-parse.h \
- tlv.c tlv.h tlv-builder.c \
+ tlv.c tlv.h tlv-builder.c tlv-parser.c \
init.c init.h \
sexputil.c \
sysutils.c sysutils.h \
@@ -160,8 +160,9 @@ endif
module_tests = t-stringhelp t-timestuff \
t-convert t-percent t-gettime t-sysutils t-sexputil \
t-session-env t-openpgp-oid t-ssh-utils \
- t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist \
+ t-mapstrings t-zb32 t-mbox-util t-iobuf t-strlist t-b64 \
t-name-value t-ccparray t-recsel t-w32-cmdline t-exechelp
+
if HAVE_W32_SYSTEM
module_tests += t-w32-reg
else
@@ -209,6 +210,7 @@ t_zb32_LDADD = $(t_common_ldadd)
t_mbox_util_LDADD = $(t_common_ldadd)
t_iobuf_LDADD = $(t_common_ldadd)
t_strlist_LDADD = $(t_common_ldadd)
+t_b64_LDADD = $(t_common_ldadd)
t_name_value_LDADD = $(t_common_ldadd)
t_ccparray_LDADD = $(t_common_ldadd)
t_recsel_LDADD = $(t_common_ldadd)
diff --git a/common/b64dec.c b/common/b64dec.c
deleted file mode 100644
index 6af494b79..000000000
--- a/common/b64dec.c
+++ /dev/null
@@ -1,254 +0,0 @@
-/* b64dec.c - Simple Base64 decoder.
- * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
- * Copyright (C) 2008, 2011, 2016 g10 Code GmbH
- *
- * This file is part of GnuPG.
- *
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This file 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 Lesser General Public License
- * along with this program; if not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "i18n.h"
-#include "util.h"
-
-
-/* The reverse base-64 list used for base-64 decoding. */
-static unsigned char const asctobin[128] =
- {
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
- 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
- 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
- 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
- 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
- 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
- };
-
-enum decoder_states
- {
- s_init, s_idle, s_lfseen, s_beginseen, s_waitheader, s_waitblank, s_begin,
- s_b64_0, s_b64_1, s_b64_2, s_b64_3,
- s_waitendtitle, s_waitend
- };
-
-
-
-/* Initialize the context for the base64 decoder. If TITLE is NULL a
- plain base64 decoding is done. If it is the empty string the
- decoder will skip everything until a "-----BEGIN " line has been
- seen, decoding ends at a "----END " line. */
-gpg_error_t
-b64dec_start (struct b64state *state, const char *title)
-{
- memset (state, 0, sizeof *state);
- if (title)
- {
- state->title = xtrystrdup (title);
- if (!state->title)
- state->lasterr = gpg_error_from_syserror ();
- else
- state->idx = s_init;
- }
- else
- state->idx = s_b64_0;
- return state->lasterr;
-}
-
-
-/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Stores the
- new length of the buffer at R_NBYTES. */
-gpg_error_t
-b64dec_proc (struct b64state *state, void *buffer, size_t length,
- size_t *r_nbytes)
-{
- enum decoder_states ds = state->idx;
- unsigned char val = state->radbuf[0];
- int pos = state->quad_count;
- char *d, *s;
-
- if (state->lasterr)
- return state->lasterr;
-
- if (state->stop_seen)
- {
- *r_nbytes = 0;
- state->lasterr = gpg_error (GPG_ERR_EOF);
- xfree (state->title);
- state->title = NULL;
- return state->lasterr;
- }
-
- for (s=d=buffer; length && !state->stop_seen; length--, s++)
- {
- again:
- switch (ds)
- {
- case s_idle:
- if (*s == '\n')
- {
- ds = s_lfseen;
- pos = 0;
- }
- break;
- case s_init:
- ds = s_lfseen;
- /* fall through */
- case s_lfseen:
- if (*s != "-----BEGIN "[pos])
- {
- ds = s_idle;
- goto again;
- }
- else if (pos == 10)
- {
- pos = 0;
- ds = s_beginseen;
- }
- else
- pos++;
- break;
- case s_beginseen:
- if (*s != "PGP "[pos])
- ds = s_begin; /* Not a PGP armor. */
- else if (pos == 3)
- ds = s_waitheader;
- else
- pos++;
- break;
- case s_waitheader:
- if (*s == '\n')
- ds = s_waitblank;
- break;
- case s_waitblank:
- if (*s == '\n')
- ds = s_b64_0; /* blank line found. */
- else if (*s == ' ' || *s == '\r' || *s == '\t')
- ; /* Ignore spaces. */
- else
- {
- /* Armor header line. Note that we don't care that our
- * FSM accepts a header prefixed with spaces. */
- ds = s_waitheader; /* Wait for next header. */
- }
- break;
- case s_begin:
- if (*s == '\n')
- ds = s_b64_0;
- break;
- case s_b64_0:
- case s_b64_1:
- case s_b64_2:
- case s_b64_3:
- {
- int c;
-
- if (*s == '-' && state->title)
- {
- /* Not a valid Base64 character: assume end
- header. */
- ds = s_waitend;
- }
- else if (*s == '=')
- {
- /* Pad character: stop */
- if (ds == s_b64_1)
- *d++ = val;
- ds = state->title? s_waitendtitle : s_waitend;
- }
- else if (*s == '\n' || *s == ' ' || *s == '\r' || *s == '\t')
- ; /* Skip white spaces. */
- else if ( (*s & 0x80)
- || (c = asctobin[*(unsigned char *)s]) == 255)
- {
- /* Skip invalid encodings. */
- state->invalid_encoding = 1;
- }
- else if (ds == s_b64_0)
- {
- val = c << 2;
- ds = s_b64_1;
- }
- else if (ds == s_b64_1)
- {
- val |= (c>>4)&3;
- *d++ = val;
- val = (c<<4)&0xf0;
- ds = s_b64_2;
- }
- else if (ds == s_b64_2)
- {
- val |= (c>>2)&15;
- *d++ = val;
- val = (c<<6)&0xc0;
- ds = s_b64_3;
- }
- else
- {
- val |= c&0x3f;
- *d++ = val;
- ds = s_b64_0;
- }
- }
- break;
- case s_waitendtitle:
- if (*s == '-')
- ds = s_waitend;
- break;
- case s_waitend:
- if ( *s == '\n')
- state->stop_seen = 1;
- break;
- default:
- BUG();
- }
- }
-
-
- state->idx = ds;
- state->radbuf[0] = val;
- state->quad_count = pos;
- *r_nbytes = (d -(char*) buffer);
- return 0;
-}
-
-
-/* This function needs to be called before releasing the decoder
- state. It may return an error code in case an encoding error has
- been found during decoding. */
-gpg_error_t
-b64dec_finish (struct b64state *state)
-{
- xfree (state->title);
- state->title = NULL;
-
- if (state->lasterr)
- return state->lasterr;
-
- return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
-}
diff --git a/common/b64enc.c b/common/b64enc.c
deleted file mode 100644
index d633048ea..000000000
--- a/common/b64enc.c
+++ /dev/null
@@ -1,422 +0,0 @@
-/* b64enc.c - Simple Base64 encoder.
- * Copyright (C) 2001, 2003, 2004, 2008, 2010,
- * 2011 Free Software Foundation, Inc.
- * Copyright (C) 2001, 2003, 2004, 2008, 2010,
- * 2011 g10 Code GmbH
- *
- * This file is part of GnuPG.
- *
- * This file is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This file 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 Lesser General Public License
- * along with this program; if not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "i18n.h"
-#include "util.h"
-
-#define B64ENC_DID_HEADER 1
-#define B64ENC_DID_TRAILER 2
-#define B64ENC_NO_LINEFEEDS 16
-#define B64ENC_USE_PGPCRC 32
-
-/* The base-64 character list */
-static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
-
-/* Stuff required to create the OpenPGP CRC. This crc_table has been
- created using this code:
-
- #include
- #include
-
- #define CRCPOLY 0x864CFB
-
- int
- main (void)
- {
- int i, j;
- uint32_t t;
- uint32_t crc_table[256];
-
- crc_table[0] = 0;
- for (i=j=0; j < 128; j++ )
- {
- t = crc_table[j];
- if ( (t & 0x00800000) )
- {
- t <<= 1;
- crc_table[i++] = t ^ CRCPOLY;
- crc_table[i++] = t;
- }
- else
- {
- t <<= 1;
- crc_table[i++] = t;
- crc_table[i++] = t ^ CRCPOLY;
- }
- }
-
- puts ("static const u32 crc_table[256] = {");
- for (i=j=0; i < 256; i++)
- {
- printf ("%s 0x%08lx", j? "":" ", (unsigned long)crc_table[i]);
- if (i != 255)
- {
- putchar (',');
- if ( ++j > 5)
- {
- j = 0;
- putchar ('\n');
- }
- }
- }
- puts ("\n};");
- return 0;
- }
-*/
-#define CRCINIT 0xB704CE
-static const u32 crc_table[256] = {
- 0x00000000, 0x00864cfb, 0x018ad50d, 0x010c99f6, 0x0393e6e1, 0x0315aa1a,
- 0x021933ec, 0x029f7f17, 0x07a18139, 0x0727cdc2, 0x062b5434, 0x06ad18cf,
- 0x043267d8, 0x04b42b23, 0x05b8b2d5, 0x053efe2e, 0x0fc54e89, 0x0f430272,
- 0x0e4f9b84, 0x0ec9d77f, 0x0c56a868, 0x0cd0e493, 0x0ddc7d65, 0x0d5a319e,
- 0x0864cfb0, 0x08e2834b, 0x09ee1abd, 0x09685646, 0x0bf72951, 0x0b7165aa,
- 0x0a7dfc5c, 0x0afbb0a7, 0x1f0cd1e9, 0x1f8a9d12, 0x1e8604e4, 0x1e00481f,
- 0x1c9f3708, 0x1c197bf3, 0x1d15e205, 0x1d93aefe, 0x18ad50d0, 0x182b1c2b,
- 0x192785dd, 0x19a1c926, 0x1b3eb631, 0x1bb8faca, 0x1ab4633c, 0x1a322fc7,
- 0x10c99f60, 0x104fd39b, 0x11434a6d, 0x11c50696, 0x135a7981, 0x13dc357a,
- 0x12d0ac8c, 0x1256e077, 0x17681e59, 0x17ee52a2, 0x16e2cb54, 0x166487af,
- 0x14fbf8b8, 0x147db443, 0x15712db5, 0x15f7614e, 0x3e19a3d2, 0x3e9fef29,
- 0x3f9376df, 0x3f153a24, 0x3d8a4533, 0x3d0c09c8, 0x3c00903e, 0x3c86dcc5,
- 0x39b822eb, 0x393e6e10, 0x3832f7e6, 0x38b4bb1d, 0x3a2bc40a, 0x3aad88f1,
- 0x3ba11107, 0x3b275dfc, 0x31dced5b, 0x315aa1a0, 0x30563856, 0x30d074ad,
- 0x324f0bba, 0x32c94741, 0x33c5deb7, 0x3343924c, 0x367d6c62, 0x36fb2099,
- 0x37f7b96f, 0x3771f594, 0x35ee8a83, 0x3568c678, 0x34645f8e, 0x34e21375,
- 0x2115723b, 0x21933ec0, 0x209fa736, 0x2019ebcd, 0x228694da, 0x2200d821,
- 0x230c41d7, 0x238a0d2c, 0x26b4f302, 0x2632bff9, 0x273e260f, 0x27b86af4,
- 0x252715e3, 0x25a15918, 0x24adc0ee, 0x242b8c15, 0x2ed03cb2, 0x2e567049,
- 0x2f5ae9bf, 0x2fdca544, 0x2d43da53, 0x2dc596a8, 0x2cc90f5e, 0x2c4f43a5,
- 0x2971bd8b, 0x29f7f170, 0x28fb6886, 0x287d247d, 0x2ae25b6a, 0x2a641791,
- 0x2b688e67, 0x2beec29c, 0x7c3347a4, 0x7cb50b5f, 0x7db992a9, 0x7d3fde52,
- 0x7fa0a145, 0x7f26edbe, 0x7e2a7448, 0x7eac38b3, 0x7b92c69d, 0x7b148a66,
- 0x7a181390, 0x7a9e5f6b, 0x7801207c, 0x78876c87, 0x798bf571, 0x790db98a,
- 0x73f6092d, 0x737045d6, 0x727cdc20, 0x72fa90db, 0x7065efcc, 0x70e3a337,
- 0x71ef3ac1, 0x7169763a, 0x74578814, 0x74d1c4ef, 0x75dd5d19, 0x755b11e2,
- 0x77c46ef5, 0x7742220e, 0x764ebbf8, 0x76c8f703, 0x633f964d, 0x63b9dab6,
- 0x62b54340, 0x62330fbb, 0x60ac70ac, 0x602a3c57, 0x6126a5a1, 0x61a0e95a,
- 0x649e1774, 0x64185b8f, 0x6514c279, 0x65928e82, 0x670df195, 0x678bbd6e,
- 0x66872498, 0x66016863, 0x6cfad8c4, 0x6c7c943f, 0x6d700dc9, 0x6df64132,
- 0x6f693e25, 0x6fef72de, 0x6ee3eb28, 0x6e65a7d3, 0x6b5b59fd, 0x6bdd1506,
- 0x6ad18cf0, 0x6a57c00b, 0x68c8bf1c, 0x684ef3e7, 0x69426a11, 0x69c426ea,
- 0x422ae476, 0x42aca88d, 0x43a0317b, 0x43267d80, 0x41b90297, 0x413f4e6c,
- 0x4033d79a, 0x40b59b61, 0x458b654f, 0x450d29b4, 0x4401b042, 0x4487fcb9,
- 0x461883ae, 0x469ecf55, 0x479256a3, 0x47141a58, 0x4defaaff, 0x4d69e604,
- 0x4c657ff2, 0x4ce33309, 0x4e7c4c1e, 0x4efa00e5, 0x4ff69913, 0x4f70d5e8,
- 0x4a4e2bc6, 0x4ac8673d, 0x4bc4fecb, 0x4b42b230, 0x49ddcd27, 0x495b81dc,
- 0x4857182a, 0x48d154d1, 0x5d26359f, 0x5da07964, 0x5cace092, 0x5c2aac69,
- 0x5eb5d37e, 0x5e339f85, 0x5f3f0673, 0x5fb94a88, 0x5a87b4a6, 0x5a01f85d,
- 0x5b0d61ab, 0x5b8b2d50, 0x59145247, 0x59921ebc, 0x589e874a, 0x5818cbb1,
- 0x52e37b16, 0x526537ed, 0x5369ae1b, 0x53efe2e0, 0x51709df7, 0x51f6d10c,
- 0x50fa48fa, 0x507c0401, 0x5542fa2f, 0x55c4b6d4, 0x54c82f22, 0x544e63d9,
- 0x56d11cce, 0x56575035, 0x575bc9c3, 0x57dd8538
-};
-
-
-static gpg_error_t
-enc_start (struct b64state *state, FILE *fp, estream_t stream,
- const char *title)
-{
- memset (state, 0, sizeof *state);
- state->fp = fp;
- state->stream = stream;
- state->lasterr = 0;
- if (title && !*title)
- state->flags |= B64ENC_NO_LINEFEEDS;
- else if (title)
- {
- if (!strncmp (title, "PGP ", 4))
- {
- state->flags |= B64ENC_USE_PGPCRC;
- state->crc = CRCINIT;
- }
- state->title = xtrystrdup (title);
- if (!state->title)
- state->lasterr = gpg_error_from_syserror ();
- }
- return state->lasterr;
-}
-
-
-/* Prepare for base-64 writing to the stream FP. If TITLE is not NULL
- and not an empty string, this string will be used as the title for
- the armor lines, with TITLE being an empty string, we don't write
- the header lines and furthermore even don't write any linefeeds.
- If TITLE starts with "PGP " the OpenPGP CRC checksum will be
- written as well. With TITLE being NULL, we merely don't write
- header but make sure that lines are not too long. Note, that we
- don't write any output unless at least one byte get written using
- b64enc_write. */
-gpg_error_t
-b64enc_start (struct b64state *state, FILE *fp, const char *title)
-{
- return enc_start (state, fp, NULL, title);
-}
-
-/* Same as b64enc_start but takes an estream. */
-gpg_error_t
-b64enc_start_es (struct b64state *state, estream_t fp, const char *title)
-{
- return enc_start (state, NULL, fp, title);
-}
-
-
-static int
-my_fputs (const char *string, struct b64state *state)
-{
- if (state->stream)
- return es_fputs (string, state->stream);
- else
- return fputs (string, state->fp);
-}
-
-
-/* Write NBYTES from BUFFER to the Base 64 stream identified by
- STATE. With BUFFER and NBYTES being 0, merely do a fflush on the
- stream. */
-gpg_error_t
-b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
-{
- unsigned char radbuf[4];
- int idx, quad_count;
- const unsigned char *p;
-
- if (state->lasterr)
- return state->lasterr;
-
- if (!nbytes)
- {
- if (buffer)
- if (state->stream? es_fflush (state->stream) : fflush (state->fp))
- goto write_error;
- return 0;
- }
-
- if (!(state->flags & B64ENC_DID_HEADER))
- {
- if (state->title)
- {
- if ( my_fputs ("-----BEGIN ", state) == EOF
- || my_fputs (state->title, state) == EOF
- || my_fputs ("-----\n", state) == EOF)
- goto write_error;
- if ( (state->flags & B64ENC_USE_PGPCRC)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
-
- state->flags |= B64ENC_DID_HEADER;
- }
-
- idx = state->idx;
- quad_count = state->quad_count;
- assert (idx < 4);
- memcpy (radbuf, state->radbuf, idx);
-
- if ( (state->flags & B64ENC_USE_PGPCRC) )
- {
- size_t n;
- u32 crc = state->crc;
-
- for (p=buffer, n=nbytes; n; p++, n-- )
- crc = ((u32)crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ *p];
- state->crc = (crc & 0x00ffffff);
- }
-
- for (p=buffer; nbytes; p++, nbytes--)
- {
- radbuf[idx++] = *p;
- if (idx > 2)
- {
- char tmp[4];
-
- tmp[0] = bintoasc[(*radbuf >> 2) & 077];
- tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
- tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
- tmp[3] = bintoasc[radbuf[2]&077];
- if (state->stream)
- {
- for (idx=0; idx < 4; idx++)
- es_putc (tmp[idx], state->stream);
- idx = 0;
- if (es_ferror (state->stream))
- goto write_error;
- }
- else
- {
- for (idx=0; idx < 4; idx++)
- putc (tmp[idx], state->fp);
- idx = 0;
- if (ferror (state->fp))
- goto write_error;
- }
- if (++quad_count >= (64/4))
- {
- quad_count = 0;
- if (!(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
- }
- }
- memcpy (state->radbuf, radbuf, idx);
- state->idx = idx;
- state->quad_count = quad_count;
- return 0;
-
- write_error:
- state->lasterr = gpg_error_from_syserror ();
- if (state->title)
- {
- xfree (state->title);
- state->title = NULL;
- }
- return state->lasterr;
-}
-
-
-gpg_error_t
-b64enc_finish (struct b64state *state)
-{
- gpg_error_t err = 0;
- unsigned char radbuf[4];
- int idx, quad_count;
- char tmp[4];
-
- if (state->lasterr)
- return state->lasterr;
-
- if (!(state->flags & B64ENC_DID_HEADER))
- goto cleanup;
-
- /* Flush the base64 encoding */
- idx = state->idx;
- quad_count = state->quad_count;
- assert (idx < 4);
- memcpy (radbuf, state->radbuf, idx);
-
- if (idx)
- {
- tmp[0] = bintoasc[(*radbuf>>2)&077];
- if (idx == 1)
- {
- tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077];
- tmp[2] = '=';
- tmp[3] = '=';
- }
- else
- {
- tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
- tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077];
- tmp[3] = '=';
- }
- if (state->stream)
- {
- for (idx=0; idx < 4; idx++)
- es_putc (tmp[idx], state->stream);
- if (es_ferror (state->stream))
- goto write_error;
- }
- else
- {
- for (idx=0; idx < 4; idx++)
- putc (tmp[idx], state->fp);
- if (ferror (state->fp))
- goto write_error;
- }
-
- if (++quad_count >= (64/4))
- {
- quad_count = 0;
- if (!(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
- }
-
- /* Finish the last line and write the trailer. */
- if (quad_count
- && !(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
-
- if ( (state->flags & B64ENC_USE_PGPCRC) )
- {
- /* Write the CRC. */
- my_fputs ("=", state);
- radbuf[0] = state->crc >>16;
- radbuf[1] = state->crc >> 8;
- radbuf[2] = state->crc;
- tmp[0] = bintoasc[(*radbuf>>2)&077];
- tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
- tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
- tmp[3] = bintoasc[radbuf[2]&077];
- if (state->stream)
- {
- for (idx=0; idx < 4; idx++)
- es_putc (tmp[idx], state->stream);
- if (es_ferror (state->stream))
- goto write_error;
- }
- else
- {
- for (idx=0; idx < 4; idx++)
- putc (tmp[idx], state->fp);
- if (ferror (state->fp))
- goto write_error;
- }
- if (!(state->flags & B64ENC_NO_LINEFEEDS)
- && my_fputs ("\n", state) == EOF)
- goto write_error;
- }
-
- if (state->title)
- {
- if ( my_fputs ("-----END ", state) == EOF
- || my_fputs (state->title, state) == EOF
- || my_fputs ("-----\n", state) == EOF)
- goto write_error;
- }
-
- goto cleanup;
-
- write_error:
- err = gpg_error_from_syserror ();
-
- cleanup:
- if (state->title)
- {
- xfree (state->title);
- state->title = NULL;
- }
- state->fp = NULL;
- state->stream = NULL;
- state->lasterr = err;
- return err;
-}
diff --git a/common/dotlock.c b/common/dotlock.c
index ab0a5a6a3..74186b776 100644
--- a/common/dotlock.c
+++ b/common/dotlock.c
@@ -1011,6 +1011,48 @@ dotlock_destroy (dotlock_t h)
}
+/* Return true if H has been taken. */
+int
+dotlock_is_locked (dotlock_t h)
+{
+ return h && !!h->locked;
+}
+
+
+/* Return the next interval to wait. WTIME and TIMEOUT are pointers
+ * to the current state and are updated by this function. The
+ * returned value might be different from the value of WTIME. */
+static int
+next_wait_interval (int *wtime, long *timeout)
+{
+ int result;
+
+ /* Wait until lock has been released. We use retry intervals of 4,
+ * 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 512, 1024, 2048ms, and
+ * so on. If wait-forever was requested we add a small random value
+ * to have different timeouts per process. */
+ if (!*wtime)
+ *wtime = 4;
+ else if (*wtime < 2048)
+ *wtime *= 2;
+ else
+ *wtime = 512;
+
+ result = *wtime;
+ if (*wtime > 8 && *timeout < 0)
+ result += ((unsigned int)getpid() % 37);
+
+ if (*timeout > 0)
+ {
+ if (result > *timeout)
+ result = *timeout;
+ *timeout -= result;
+ }
+
+ return result;
+}
+
+
#ifdef HAVE_POSIX_SYSTEM
/* Unix specific code of make_dotlock. Returns 0 on success and -1 on
@@ -1170,27 +1212,14 @@ dotlock_take_unix (dotlock_t h, long timeout)
if (timeout)
{
struct timeval tv;
+ int wtimereal;
- /* Wait until lock has been released. We use increasing retry
- intervals of 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s
- but reset it if the lock owner meanwhile changed. */
- if (!wtime || ownerchanged)
- wtime = 50;
- else if (wtime < 800)
- wtime *= 2;
- else if (wtime == 800)
- wtime = 2000;
- else if (wtime < 8000)
- wtime *= 2;
+ if (ownerchanged)
+ wtime = 0; /* Reset because owner chnaged. */
- if (timeout > 0)
- {
- if (wtime > timeout)
- wtime = timeout;
- timeout -= wtime;
- }
+ wtimereal = next_wait_interval (&wtime, &timeout);
- sumtime += wtime;
+ sumtime += wtimereal;
if (sumtime >= 1500)
{
sumtime = 0;
@@ -1198,9 +1227,8 @@ dotlock_take_unix (dotlock_t h, long timeout)
pid, maybe_dead, maybe_deadlock(h)? _("(deadlock?) "):"");
}
-
- tv.tv_sec = wtime / 1000;
- tv.tv_usec = (wtime % 1000) * 1000;
+ tv.tv_sec = wtimereal / 1000;
+ tv.tv_usec = (wtimereal % 1000) * 1000;
select (0, NULL, NULL, NULL, &tv);
goto again;
}
@@ -1242,28 +1270,14 @@ dotlock_take_w32 (dotlock_t h, long timeout)
if (timeout)
{
- /* Wait until lock has been released. We use retry intervals of
- 50ms, 100ms, 200ms, 400ms, 800ms, 2s, 4s and 8s. */
- if (!wtime)
- wtime = 50;
- else if (wtime < 800)
- wtime *= 2;
- else if (wtime == 800)
- wtime = 2000;
- else if (wtime < 8000)
- wtime *= 2;
+ int wtimereal;
- if (timeout > 0)
- {
- if (wtime > timeout)
- wtime = timeout;
- timeout -= wtime;
- }
+ wtimereal = next_wait_interval (&wtime, &timeout);
if (wtime >= 800)
my_info_1 (_("waiting for lock %s...\n"), h->lockname);
- Sleep (wtime);
+ Sleep (wtimereal);
goto again;
}
diff --git a/common/gettime.c b/common/gettime.c
index 2a9b71779..136c47ca7 100644
--- a/common/gettime.c
+++ b/common/gettime.c
@@ -37,6 +37,11 @@
#ifdef HAVE_LANGINFO_H
#include
#endif
+#ifdef HAVE_W32_SYSTEM
+# define WIN32_LEAN_AND_MEAN
+# include
+#endif /*!HAVE_W32_SYSTEM*/
+#include /* We use uint64_t. */
#include "util.h"
#include "i18n.h"
@@ -61,6 +66,111 @@ static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode;
#define JD_DIFF 1721060L
+
+/*
+ timegm() is a GNU function that might not be available everywhere.
+ It's basically the inverse of gmtime() - you give it a struct tm,
+ and get back a time_t. It differs from mktime() in that it handles
+ the case where the struct tm is UTC and the local environment isn't.
+
+ Note, that this replacement implementation might not be thread-safe!
+
+ Some BSDs don't handle the putenv("foo") case properly, so we use
+ unsetenv if the platform has it to remove environment variables.
+*/
+#ifndef HAVE_TIMEGM
+time_t
+timegm (struct tm *tm)
+{
+#ifdef HAVE_W32_SYSTEM
+ uint64_t val = timegm_u64 (tm);
+ if (val == (uint64_t)(-1))
+ return (time_t)(-1);
+ return (time_t)val;
+#else /* (Non thread safe implementation!) */
+
+ time_t answer;
+ char *zone;
+
+ zone=getenv("TZ");
+ putenv("TZ=UTC");
+ tzset();
+ answer=mktime(tm);
+ if(zone)
+ {
+ static char *old_zone;
+
+ if (!old_zone)
+ {
+ old_zone = malloc(3+strlen(zone)+1);
+ if (old_zone)
+ {
+ strcpy(old_zone,"TZ=");
+ strcat(old_zone,zone);
+ }
+ }
+ if (old_zone)
+ putenv (old_zone);
+ }
+ else
+ gnupg_unsetenv("TZ");
+
+ tzset();
+ return answer;
+#endif
+}
+#endif /*!HAVE_TIMEGM*/
+
+
+/* Version of the GNU timegm which returns an unsigned 64 bit integer
+ * instead of the usually signed time_t. On error (uint64_t)(-1) is
+ * returned. This function is mostly here becuase on 32 bit Windows
+ * we have an internal API to get the system time even after
+ * 2023-01-19. For 32 bit Unix we need to suffer from the too short
+ * time_t and no system function to construct the time from a tm. */
+uint64_t
+timegm_u64 (struct tm *tm)
+{
+#ifdef HAVE_W32_SYSTEM
+ /* This one is thread safe. */
+ SYSTEMTIME st;
+ FILETIME ft;
+ unsigned long long cnsecs;
+
+ st.wYear = tm->tm_year + 1900;
+ st.wMonth = tm->tm_mon + 1;
+ st.wDay = tm->tm_mday;
+ st.wHour = tm->tm_hour;
+ st.wMinute = tm->tm_min;
+ st.wSecond = tm->tm_sec;
+ st.wMilliseconds = 0; /* Not available. */
+ st.wDayOfWeek = 0; /* Ignored. */
+
+ /* System time is UTC thus the conversion is pretty easy. */
+ if (!SystemTimeToFileTime (&st, &ft))
+ {
+ gpg_err_set_errno (EINVAL);
+ return (uint64_t)(-1);
+ }
+
+ cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
+ | ft.dwLowDateTime);
+ cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */
+ return (uint64_t)(cnsecs / 10000000ULL);
+
+#else /*Unix*/
+
+ time_t t = timegm (tm);
+ if (t == (time_t)(-1))
+ return (uint64_t)(-1);
+ if ((int64_t)t < 0)
+ return (uint64_t)(-1);
+ return (uint64_t)t;
+
+#endif /*Unix*/
+}
+
+
/* Wrapper for the time(3). We use this here so we can fake the time
for tests */
time_t
@@ -172,6 +282,28 @@ make_timestamp (void)
}
+/* Specialized version of atoi which returns an u32 instead of an int
+ * and caps the result at 2^32-2. Leading white space is skipped,
+ * scanning stops at at the first non-convertable byte. Note that we
+ * do not cap at 2^32-1 because that value is often used as error
+ * return. */
+u32
+scan_secondsstr (const char *string)
+{
+ uint64_t value = 0;
+
+ while (*string == ' ' || *string == '\t')
+ string++;
+ for (; *string >= '0' && *string <= '9'; string++)
+ {
+ value *= 10;
+ value += atoi_1 (string);
+ if (value >= (u32)(-1))
+ return (u32)(-1) - 1;
+ }
+ return (u32)value;
+}
+
/****************
* Scan a date string and return a timestamp.
@@ -208,7 +340,21 @@ scan_isodatestr( const char *string )
tmbuf.tm_isdst = -1;
stamp = mktime( &tmbuf );
if( stamp == (time_t)-1 )
- return 0;
+ {
+ /* mktime did not work. Construct an ISO timestring for noon
+ * of the given day instead. We keep the use of mktime for 64
+ * bit system to limit the risk of regressions. */
+ gnupg_isotime_t isobuf;
+ uint64_t tmp64;
+
+ snprintf (isobuf, 16, "%04d%02d%02dT120000", year, month, day);
+ tmp64 = isotime2epoch_u64 (isobuf);
+ if (tmp64 == (uint64_t)(-1))
+ return 0; /* Error. */
+ if (tmp64 >= (u32)(-1))
+ return 0; /* Error. */
+ return (u32)tmp64;
+ }
return stamp;
}
@@ -363,18 +509,14 @@ string2isotime (gnupg_isotime_t atime, const char *string)
}
-/* Scan an ISO timestamp and return an Epoch based timestamp. The
- only supported format is "yyyymmddThhmmss[Z]" delimited by white
- space, nul, a colon or a comma. Returns (time_t)(-1) for an
- invalid string. */
-time_t
-isotime2epoch (const char *string)
+/* Helper for isotime2epoch. Returns 0 on success. */
+static int
+isotime_make_tm (const char *string, struct tm *tmbuf)
{
int year, month, day, hour, minu, sec;
- struct tm tmbuf;
if (!isotime_p (string))
- return (time_t)(-1);
+ return -1;
year = atoi_4 (string);
month = atoi_2 (string + 4);
@@ -386,20 +528,48 @@ isotime2epoch (const char *string)
/* Basic checks. */
if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31
|| hour > 23 || minu > 59 || sec > 61 )
+ return -1;
+
+ memset (tmbuf, 0, sizeof *tmbuf);
+ tmbuf->tm_sec = sec;
+ tmbuf->tm_min = minu;
+ tmbuf->tm_hour = hour;
+ tmbuf->tm_mday = day;
+ tmbuf->tm_mon = month-1;
+ tmbuf->tm_year = year - 1900;
+ tmbuf->tm_isdst = -1;
+ return 0;
+}
+
+
+/* Scan an ISO timestamp and return an Epoch based timestamp. The
+ only supported format is "yyyymmddThhmmss[Z]" delimited by white
+ space, nul, a colon or a comma. Returns (time_t)(-1) for an
+ invalid string. */
+time_t
+isotime2epoch (const char *string)
+{
+ struct tm tmbuf;
+
+ if (isotime_make_tm (string, &tmbuf))
return (time_t)(-1);
- memset (&tmbuf, 0, sizeof tmbuf);
- tmbuf.tm_sec = sec;
- tmbuf.tm_min = minu;
- tmbuf.tm_hour = hour;
- tmbuf.tm_mday = day;
- tmbuf.tm_mon = month-1;
- tmbuf.tm_year = year - 1900;
- tmbuf.tm_isdst = -1;
return timegm (&tmbuf);
}
+uint64_t
+isotime2epoch_u64 (const char *string)
+{
+ struct tm tmbuf;
+
+ if (isotime_make_tm (string, &tmbuf))
+ return (uint64_t)(-1);
+
+ return timegm_u64 (&tmbuf);
+}
+
+
/* Convert an Epoch time to an iso time stamp. */
void
epoch2isotime (gnupg_isotime_t timebuf, time_t atime)
@@ -453,41 +623,6 @@ isodate_human_to_tm (const char *string, struct tm *t)
}
-/* This function is a copy of gpgme/src/conversion.c:_gpgme_timegm.
- If you change it, then update the other one too. */
-#ifdef HAVE_W32_SYSTEM
-static time_t
-_win32_timegm (struct tm *tm)
-{
- /* This one is thread safe. */
- SYSTEMTIME st;
- FILETIME ft;
- unsigned long long cnsecs;
-
- st.wYear = tm->tm_year + 1900;
- st.wMonth = tm->tm_mon + 1;
- st.wDay = tm->tm_mday;
- st.wHour = tm->tm_hour;
- st.wMinute = tm->tm_min;
- st.wSecond = tm->tm_sec;
- st.wMilliseconds = 0; /* Not available. */
- st.wDayOfWeek = 0; /* Ignored. */
-
- /* System time is UTC thus the conversion is pretty easy. */
- if (!SystemTimeToFileTime (&st, &ft))
- {
- gpg_err_set_errno (EINVAL);
- return (time_t)(-1);
- }
-
- cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
- | ft.dwLowDateTime);
- cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */
- return (time_t)(cnsecs / 10000000ULL);
-}
-#endif
-
-
/* Parse the string TIMESTAMP into a time_t. The string may either be
seconds since Epoch or in the ISO 8601 format like
"20390815T143012". Returns 0 for an empty string or seconds since
@@ -496,7 +631,11 @@ _win32_timegm (struct tm *tm)
This function is a copy of
gpgme/src/conversion.c:_gpgme_parse_timestamp. If you change it,
- then update the other one too. */
+ then update the other one too.
+
+ FIXME: Replace users of this function by one of the more modern
+ functions or change the return type to u64.
+*/
time_t
parse_timestamp (const char *timestamp, char **endp)
{
@@ -532,24 +671,7 @@ parse_timestamp (const char *timestamp, char **endp)
buf.tm_min = atoi_2 (timestamp+11);
buf.tm_sec = atoi_2 (timestamp+13);
-#ifdef HAVE_W32_SYSTEM
- return _win32_timegm (&buf);
-#else
-#ifdef HAVE_TIMEGM
return timegm (&buf);
-#else
- {
- time_t tim;
-
- putenv ("TZ=UTC");
- tim = mktime (&buf);
-#ifdef __GNUC__
-#warning fixme: we must somehow reset TZ here. It is not threadsafe anyway.
-#endif
- return tim;
- }
-#endif /* !HAVE_TIMEGM */
-#endif /* !HAVE_W32_SYSTEM */
}
else
return (time_t)strtoul (timestamp, endp, 10);
@@ -728,7 +850,7 @@ asctimestamp (u32 stamp)
* 2018 has a lot of additional support but that will for sure
* break other things. We should move to ISO strings to get
* rid of such problems. */
- setlocale (LC_TIME, "");
+ setlocale (LC_TIME, ".UTF8");
done = 1;
/* log_debug ("LC_ALL now '%s'\n", setlocale (LC_ALL, NULL)); */
/* log_debug ("LC_TIME now '%s'\n", setlocale (LC_TIME, NULL)); */
diff --git a/common/gettime.h b/common/gettime.h
index 4f7199f92..e216ddd36 100644
--- a/common/gettime.h
+++ b/common/gettime.h
@@ -32,7 +32,7 @@
#include /* We need time_t. */
#include /* We need gpg_error_t. */
-
+#include /* We use uint64_t. */
/* A type to hold the ISO time. Note that this is the same as
the KSBA type ksba_isotime_t. */
@@ -43,6 +43,11 @@ typedef char gnupg_isotime_t[16];
#define GNUPG_ISOTIME_NONE \
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+#ifndef HAVE_TIMEGM
+time_t timegm (struct tm *tm);
+#endif /*!HAVE_TIMEGM*/
+uint64_t timegm_u64 (struct tm *tm);
+
time_t gnupg_get_time (void);
struct tm *gnupg_gmtime (const time_t *timep, struct tm *result);
void gnupg_get_isotime (gnupg_isotime_t timebuf);
@@ -51,11 +56,13 @@ int gnupg_faked_time_p (void);
u32 make_timestamp (void);
char *elapsed_time_string (time_t since, time_t now);
+u32 scan_secondsstr (const char *string);
u32 scan_isodatestr (const char *string);
int isotime_p (const char *string);
int isotime_human_p (const char *string, int date_only);
size_t string2isotime (gnupg_isotime_t atime, const char *string);
time_t isotime2epoch (const char *string);
+uint64_t isotime2epoch_u64 (const char *string);
void epoch2isotime (gnupg_isotime_t timebuf, time_t atime);
int isodate_human_to_tm (const char *string, struct tm *t);
time_t parse_timestamp (const char *timestamp, char **endp);
diff --git a/common/mbox-util.c b/common/mbox-util.c
index a9086a3f5..fb6d06780 100644
--- a/common/mbox-util.c
+++ b/common/mbox-util.c
@@ -57,35 +57,6 @@ mem_count_chr (const void *buffer, int c, size_t length)
}
-/* This is a case-sensitive version of our memistr. I wonder why no
- standard function memstr exists but I better do not use the name
- memstr to avoid future conflicts. */
-static const char *
-my_memstr (const void *buffer, size_t buflen, const char *sub)
-{
- const unsigned char *buf = buffer;
- const unsigned char *t = (const unsigned char *)buf;
- const unsigned char *s = (const unsigned char *)sub;
- size_t n = buflen;
-
- for ( ; n ; t++, n-- )
- {
- if (*t == *s)
- {
- for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
- ;
- if (!*s)
- return (const char*)buf;
- t = (const unsigned char *)buf;
- s = (const unsigned char *)sub ;
- n = buflen;
- }
- }
- return NULL;
-}
-
-
-
static int
string_has_ctrl_or_space (const char *string)
{
@@ -159,7 +130,7 @@ is_valid_mailbox_mem (const void *name_arg, size_t namelen)
|| *name == '@'
|| name[namelen-1] == '@'
|| name[namelen-1] == '.'
- || my_memstr (name, namelen, ".."));
+ || gnupg_memstr (name, namelen, ".."));
}
diff --git a/common/miscellaneous.c b/common/miscellaneous.c
index 1a090b1f5..28d3e7a2e 100644
--- a/common/miscellaneous.c
+++ b/common/miscellaneous.c
@@ -687,3 +687,53 @@ parse_compatibility_flags (const char *string, unsigned int *flagvar,
*flagvar |= result;
return 0;
}
+
+
+/* Convert STRING consisting of base64 characters into its binary
+ * representation and store the result in a newly allocated buffer at
+ * R_BUFFER with its length at R_BUFLEN. If TITLE is NULL a plain
+ * base64 decoding is done. If it is the empty string the decoder
+ * will skip everything until a "-----BEGIN " line has been seen,
+ * decoding then ends at a "----END " line. On failure the function
+ * returns an error code and sets R_BUFFER to NULL. If the decoded
+ * data has a length of 0 a dummy buffer will still be allocated and
+ * the length is set to 0. */
+gpg_error_t
+b64decode (const char *string, const char *title,
+ void **r_buffer, size_t *r_buflen)
+{
+ gpg_error_t err;
+ gpgrt_b64state_t state;
+ size_t nbytes;
+ char *buffer;
+
+ *r_buffer = NULL;
+ *r_buflen = 0;
+
+ buffer = xtrystrdup (string);
+ if (!buffer)
+ return gpg_error_from_syserror();
+
+ state = gpgrt_b64dec_start (title);
+ if (!state)
+ {
+ err = gpg_error_from_syserror ();
+ xfree (buffer);
+ return err;
+ }
+ err = gpgrt_b64dec_proc (state, buffer, strlen (buffer), &nbytes);
+ if (!err)
+ {
+ err = gpgrt_b64dec_finish (state);
+ state = NULL;
+ }
+ if (err)
+ xfree (buffer);
+ else
+ {
+ *r_buffer = buffer;
+ *r_buflen = nbytes;
+ }
+ gpgrt_b64dec_finish (state); /* Make sure it is released. */
+ return err;
+}
diff --git a/common/mischelp.c b/common/mischelp.c
index ee8500297..ef70c9d83 100644
--- a/common/mischelp.c
+++ b/common/mischelp.c
@@ -126,80 +126,3 @@ same_file_p (const char *name1, const char *name2)
}
return yes;
}
-
-
-/*
- timegm() is a GNU function that might not be available everywhere.
- It's basically the inverse of gmtime() - you give it a struct tm,
- and get back a time_t. It differs from mktime() in that it handles
- the case where the struct tm is UTC and the local environment isn't.
-
- Note, that this replacement implementation might not be thread-safe!
-
- Some BSDs don't handle the putenv("foo") case properly, so we use
- unsetenv if the platform has it to remove environment variables.
-*/
-#ifndef HAVE_TIMEGM
-time_t
-timegm (struct tm *tm)
-{
-#ifdef HAVE_W32_SYSTEM
- /* This one is thread safe. */
- SYSTEMTIME st;
- FILETIME ft;
- unsigned long long cnsecs;
-
- st.wYear = tm->tm_year + 1900;
- st.wMonth = tm->tm_mon + 1;
- st.wDay = tm->tm_mday;
- st.wHour = tm->tm_hour;
- st.wMinute = tm->tm_min;
- st.wSecond = tm->tm_sec;
- st.wMilliseconds = 0; /* Not available. */
- st.wDayOfWeek = 0; /* Ignored. */
-
- /* System time is UTC thus the conversion is pretty easy. */
- if (!SystemTimeToFileTime (&st, &ft))
- {
- gpg_err_set_errno (EINVAL);
- return (time_t)(-1);
- }
-
- cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
- | ft.dwLowDateTime);
- cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01. */
- return (time_t)(cnsecs / 10000000ULL);
-
-#else /* (Non thread safe implementation!) */
-
- time_t answer;
- char *zone;
-
- zone=getenv("TZ");
- putenv("TZ=UTC");
- tzset();
- answer=mktime(tm);
- if(zone)
- {
- static char *old_zone;
-
- if (!old_zone)
- {
- old_zone = malloc(3+strlen(zone)+1);
- if (old_zone)
- {
- strcpy(old_zone,"TZ=");
- strcat(old_zone,zone);
- }
- }
- if (old_zone)
- putenv (old_zone);
- }
- else
- gnupg_unsetenv("TZ");
-
- tzset();
- return answer;
-#endif
-}
-#endif /*!HAVE_TIMEGM*/
diff --git a/common/mischelp.h b/common/mischelp.h
index bdee5a443..7359ec29e 100644
--- a/common/mischelp.h
+++ b/common/mischelp.h
@@ -38,12 +38,6 @@
int same_file_p (const char *name1, const char *name2);
-#ifndef HAVE_TIMEGM
-#include
-time_t timegm (struct tm *tm);
-#endif /*!HAVE_TIMEGM*/
-
-
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
#define DIMof(type,member) DIM(((type *)0)->member)
diff --git a/common/recsel.c b/common/recsel.c
index ea0858c84..fa3debaaf 100644
--- a/common/recsel.c
+++ b/common/recsel.c
@@ -85,37 +85,6 @@ my_error (gpg_err_code_t ec)
}
-/* This is a case-sensitive version of our memistr. I wonder why no
- * standard function memstr exists but I better do not use the name
- * memstr to avoid future conflicts.
- *
- * FIXME: Move this to a stringhelp.c
- */
-static const char *
-my_memstr (const void *buffer, size_t buflen, const char *sub)
-{
- const unsigned char *buf = buffer;
- const unsigned char *t = (const unsigned char *)buf;
- const unsigned char *s = (const unsigned char *)sub;
- size_t n = buflen;
-
- for ( ; n ; t++, n-- )
- {
- if (*t == *s)
- {
- for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
- ;
- if (!*s)
- return (const char*)buf;
- t = (const unsigned char *)buf;
- s = (const unsigned char *)sub ;
- n = buflen;
- }
- }
- return NULL;
-}
-
-
/* Return a pointer to the next logical connection operator or NULL if
* none. */
static char *
@@ -560,7 +529,7 @@ recsel_select (recsel_expr_t selector,
break;
case SELECT_SUB:
if (se->xcase)
- result = !!my_memstr (value, valuelen, se->value);
+ result = !!gnupg_memstr (value, valuelen, se->value);
else
result = !!memistr (value, valuelen, se->value);
break;
diff --git a/common/stringhelp.c b/common/stringhelp.c
index 1049c78e2..9a2265258 100644
--- a/common/stringhelp.c
+++ b/common/stringhelp.c
@@ -161,6 +161,35 @@ ascii_memistr ( const void *buffer, size_t buflen, const char *sub )
}
+/* This is a case-sensitive version of our memistr. I wonder why no
+ * standard function memstr exists but we better do not use the name
+ * memstr to avoid future conflicts.
+ */
+const char *
+gnupg_memstr (const void *buffer, size_t buflen, const char *sub)
+{
+ const unsigned char *buf = buffer;
+ const unsigned char *t = (const unsigned char *)buf;
+ const unsigned char *s = (const unsigned char *)sub;
+ size_t n = buflen;
+
+ for ( ; n ; t++, n-- )
+ {
+ if (*t == *s)
+ {
+ for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
+ ;
+ if (!*s)
+ return (const char*)buf;
+ t = (const unsigned char *)buf;
+ s = (const unsigned char *)sub ;
+ n = buflen;
+ }
+ }
+ return NULL;
+}
+
+
/* This function is similar to strncpy(). However it won't copy more
* than N - 1 characters and makes sure that a '\0' is appended. With
* N given as 0, nothing will happen. With DEST given as NULL, memory
@@ -696,7 +725,7 @@ compare_filenames (const char *a, const char *b)
/* Convert a base-10 number in STRING into a 64 bit unsigned int
* value. Leading white spaces are skipped but no error checking is
- * done. Thus it is similar to atoi(). */
+ * done. Thus it is similar to atoi(). See also scan_secondsstr. */
uint64_t
string_to_u64 (const char *string)
{
diff --git a/common/stringhelp.h b/common/stringhelp.h
index cd185e49a..d93373ec5 100644
--- a/common/stringhelp.h
+++ b/common/stringhelp.h
@@ -40,6 +40,7 @@
char *has_leading_keyword (const char *string, const char *keyword);
const char *memistr (const void *buf, size_t buflen, const char *sub);
+const char *gnupg_memstr (const void *buffer, size_t buflen, const char *sub);
char *mem2str( char *, const void *, size_t);
char *trim_spaces( char *string );
char *ascii_trim_spaces (char *string);
diff --git a/common/sysutils.c b/common/sysutils.c
index 80aa3a04a..6c7d616b9 100644
--- a/common/sysutils.c
+++ b/common/sysutils.c
@@ -933,7 +933,12 @@ gnupg_remove (const char *fname)
return -1;
return 0;
#else
- return remove (fname);
+ /* It is common to use /dev/null for testing. We better don't
+ * remove that file. */
+ if (fname && !strcmp (fname, "/dev/null"))
+ return 0;
+ else
+ return remove (fname);
#endif
}
diff --git a/common/t-b64.c b/common/t-b64.c
index 3b6387246..783dea5cc 100644
--- a/common/t-b64.c
+++ b/common/t-b64.c
@@ -1,30 +1,23 @@
-/* t-b64.c - Module tests for b64enc.c and b64dec.c
- * Copyright (C) 2008 Free Software Foundation, Inc.
+/* t-b64.c - Module tests for b64decodec
+ * Copyright (C) 2023 g10 Code GmbH
*
* 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.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
*
- * GnuPG is distributed in the hope that it will be useful,
+ * This file 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
+ * You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
+ * SPDX-License-Identifier: LGPL-2.1-or-later
*/
-/*
-
- As of now this is only a test program for manual tests.
-
- */
-
-
-
#include
#include
#include
@@ -36,121 +29,117 @@
__FILE__,__LINE__, (a)); \
errcount++; \
} while(0)
+#define oops() do { fprintf (stderr, "%s:%d: ooops\n", \
+ __FILE__,__LINE__); \
+ exit (2); \
+ } while(0)
static int verbose;
static int errcount;
-static void
-test_b64enc_pgp (const char *string)
+
+/* Convert STRING consisting of hex characters into its binary
+ * representation and return it as an allocated buffer. The valid
+ * length of the buffer is returned at R_LENGTH. The string is
+ * delimited by end of string. The function returns NULL on
+ * error. */
+static void *
+hex2buffer (const char *string, size_t *r_length)
{
- gpg_error_t err;
- struct b64state state;
+ const char *s;
+ unsigned char *buffer;
+ size_t length;
- if (!string)
- string = "a";
-
- err = b64enc_start (&state, stdout, "PGP MESSAGE");
- if (err)
- fail (1);
-
- err = b64enc_write (&state, string, strlen (string));
- if (err)
- fail (2);
-
- err = b64enc_finish (&state);
- if (err)
- fail (3);
-
- pass ();
+ buffer = xmalloc (strlen(string)/2+1);
+ length = 0;
+ for (s=string; *s; s +=2 )
+ {
+ if (!hexdigitp (s) || !hexdigitp (s+1))
+ return NULL; /* Invalid hex digits. */
+ ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+ }
+ *r_length = length;
+ return buffer;
}
static void
-test_b64enc_file (const char *fname)
+test_b64decode (void)
{
+ static struct {
+ const char *string; /* String to test. */
+ const char *title; /* title parameter. */
+ gpg_error_t err; /* expected error. */
+ const char *datastr; /* Expected data (hex encoded) */
+ } tests[] = {
+ { "YQ==", NULL, 0,
+ "61" },
+ { "YWE==", NULL, 0,
+ "6161" },
+ { "YWFh", NULL, 0,
+ "616161" },
+ { "YWFhYQ==", NULL, 0,
+ "61616161" },
+ { "YWJjZA==", NULL, 0,
+ "61626364" },
+ { "AA=", NULL, 0,
+ "00" },
+ { "AAEA=", NULL, 0,
+ "000100" },
+ { "/w==", NULL, 0,
+ "ff" },
+ { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", NULL, 0,
+ "a1143012a0030a0103a10b06092a864882f712010202" },
+ { "oRQwEqADCgEDoQsGCSqGSIL3EgECA-==", NULL, GPG_ERR_BAD_DATA,
+ "a1143012a0030a0103a10b06092a864882f712010202" },
+ { "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==", "", 0,
+ "" },
+ { "-----BEGIN PGP\n\n"
+ "oRQwEqADCgEDoQsGCSqGSIL3EgECAg==\n"
+ "-----END PGP\n", "", 0,
+ "a1143012a0030a0103a10b06092a864882f712010202" },
+
+ { "", NULL, 0,
+ "" }
+ };
+ int tidx;
gpg_error_t err;
- struct b64state state;
- FILE *fp;
- char buffer[50];
- size_t nread;
+ void *data = NULL;
+ size_t datalen;
+ char *wantdata = NULL;
+ size_t wantdatalen;
- fp = fname ? fopen (fname, "r") : stdin;
- if (!fp)
+ for (tidx = 0; tidx < DIM(tests); tidx++)
{
- fprintf (stderr, "%s:%d: can't open '%s': %s\n",
- __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno));
- fail (0);
+ xfree (wantdata);
+ if (!(wantdata = hex2buffer (tests[tidx].datastr, &wantdatalen)))
+ oops ();
+ xfree (data);
+ err = b64decode (tests[tidx].string, tests[tidx].title, &data, &datalen);
+ if (verbose)
+ fprintf (stderr, "%s:%d: test %d, err=%d, datalen=%zu\n",
+ __FILE__, __LINE__, tidx, err, datalen);
+ if (gpg_err_code (err) != tests[tidx].err)
+ fail (tidx);
+ else if (err)
+ pass ();
+ else if (wantdatalen != datalen)
+ fail (tidx);
+ else if (memcmp (wantdata, data, datalen))
+ fail (tidx);
+ else
+ pass ();
}
-
- err = b64enc_start (&state, stdout, "DATA");
- if (err)
- fail (1);
-
- while ( (nread = fread (buffer, 1, sizeof buffer, fp)) )
- {
- err = b64enc_write (&state, buffer, nread);
- if (err)
- fail (2);
- }
-
- err = b64enc_finish (&state);
- if (err)
- fail (3);
-
- fclose (fp);
- pass ();
+ xfree (wantdata);
+ xfree (data);
}
-static void
-test_b64dec_file (const char *fname)
-{
- gpg_error_t err;
- struct b64state state;
- FILE *fp;
- char buffer[50];
- size_t nread, nbytes;
-
- fp = fname ? fopen (fname, "r") : stdin;
- if (!fp)
- {
- fprintf (stderr, "%s:%d: can't open '%s': %s\n",
- __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno));
- fail (0);
- }
-
- err = b64dec_start (&state, "");
- if (err)
- fail (1);
-
- while ( (nread = fread (buffer, 1, sizeof buffer, fp)) )
- {
- err = b64dec_proc (&state, buffer, nread, &nbytes);
- if (err)
- {
- if (gpg_err_code (err) == GPG_ERR_EOF)
- break;
- fail (2);
- }
- else if (nbytes)
- fwrite (buffer, 1, nbytes, stdout);
- }
-
- err = b64dec_finish (&state);
- if (err)
- fail (3);
-
- fclose (fp);
- pass ();
-}
int
main (int argc, char **argv)
{
- int do_encode = 0;
- int do_decode = 0;
-
if (argc)
{ argc--; argv++; }
if (argc && !strcmp (argv[0], "--verbose"))
@@ -159,23 +148,7 @@ main (int argc, char **argv)
argc--; argv++;
}
- if (argc && !strcmp (argv[0], "--encode"))
- {
- do_encode = 1;
- argc--; argv++;
- }
- else if (argc && !strcmp (argv[0], "--decode"))
- {
- do_decode = 1;
- argc--; argv++;
- }
-
- if (do_encode)
- test_b64enc_file (argc? *argv: NULL);
- else if (do_decode)
- test_b64dec_file (argc? *argv: NULL);
- else
- test_b64enc_pgp (argc? *argv: NULL);
+ test_b64decode ();
return !!errcount;
}
diff --git a/common/t-gettime.c b/common/t-gettime.c
index 13cb1a2f7..76c305204 100644
--- a/common/t-gettime.c
+++ b/common/t-gettime.c
@@ -43,6 +43,56 @@ static int errcount;
#define INVALID ((time_t)(-1))
+static void
+test_scan_secondsstr (void)
+{
+ struct { const char *string; u32 expected; } array [] = {
+ { "", 0 },
+ { "0", 0 },
+ { " 0", 0 },
+ { " 0x", 0 },
+ { " 1", 1 },
+ { "-1", 0 },
+ { " -1", 0 },
+ { "2", 2 },
+ { "11", 11 },
+ { "011", 11 },
+ { "3600 ", 3600 },
+ { "65535", 65535 },
+ { "65536", 65536 },
+ { "65537", 65537 },
+ { "4294967289", 4294967289 },
+ { "4294967290", 4294967290 },
+ { "4294967293", 4294967293 },
+ { "4294967295", 4294967294 },
+ { "4294967296", 4294967294 },
+ { "4294967297", 4294967294 },
+ { "4294967298", 4294967294 },
+ { "4294967299", 4294967294 },
+ { "4294967300", 4294967294 },
+ { "5294967300", 4294967294 },
+ { "9999999999", 4294967294 },
+ { "99999999999",4294967294 },
+ { NULL, 0 }
+ };
+ int idx;
+ u32 val;
+
+ for (idx=0; array[idx].string; idx++)
+ {
+ val = scan_secondsstr (array[idx].string);
+ if (val != array[idx].expected )
+ {
+ fail (idx);
+ if (verbose)
+ fprintf (stderr, "string '%s' exp: %ld got: %ld\n",
+ array[idx].string, (unsigned long)array[idx].expected,
+ (unsigned long)val);
+ }
+ }
+}
+
+
static void
test_isotime2epoch (void)
{
@@ -103,7 +153,6 @@ test_isotime2epoch (void)
}
-
static void
test_string2isotime (void)
{
@@ -269,6 +318,7 @@ main (int argc, char **argv)
if (argc > 1 && !strcmp (argv[1], "--verbose"))
verbose = 1;
+ test_scan_secondsstr ();
test_isotime2epoch ();
test_string2isotime ();
test_isodate_human_to_tm ();
diff --git a/common/tlv-parser.c b/common/tlv-parser.c
new file mode 100644
index 000000000..c9b33d4b6
--- /dev/null
+++ b/common/tlv-parser.c
@@ -0,0 +1,788 @@
+/* tlv-parser.c - Parse BER encoded objects
+ * Copyright (C) 2023 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This file 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 Lesser General Public License
+ * along with this program; if not, see .
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include "util.h"
+#include "tlv.h"
+
+
+#define TLV_MAX_DEPTH 25
+
+
+struct bufferlist_s
+{
+ struct bufferlist_s *next;
+ char *buffer;
+};
+
+
+/* An object to control the ASN.1 parsing. */
+struct tlv_parser_s
+{
+ /* The orginal buffer with the entire pkcs#12 object and its length. */
+ const unsigned char *origbuffer;
+ size_t origbufsize;
+
+ /* The current buffer we are working on and its length. */
+ const unsigned char *buffer;
+ size_t bufsize;
+
+ int in_ndef; /* Flag indicating that we are in a NDEF. */
+ int pending; /* The last tlv_next has not yet been processed. */
+
+ struct tag_info ti; /* The current tag. */
+ gpg_error_t lasterr; /* Last error from tlv function. */
+ const char *lastfunc;/* Name of last called function. */
+ int verbosity; /* Arg from tlv_parser_new. */
+
+ struct bufferlist_s *bufferlist; /* To keep track of malloced buffers. */
+
+ unsigned int stacklen; /* Used size of the stack. */
+ struct {
+ const unsigned char *buffer; /* Saved value of BUFFER. */
+ size_t bufsize; /* Saved value of BUFSIZE. */
+ size_t length; /* Length of the container (ti.length). */
+ int in_ndef; /* Saved IN_NDEF flag (ti.ndef). */
+ } stack[TLV_MAX_DEPTH];
+};
+
+
+static unsigned char *cram_octet_string (const unsigned char *input,
+ size_t length, size_t *r_newlength);
+static int need_octet_string_cramming (const unsigned char *input,
+ size_t length);
+
+
+
+void
+_tlv_parser_dump_tag (const char *text, int lno, tlv_parser_t tlv)
+{
+ struct tag_info *ti;
+
+ if (!tlv || tlv->verbosity < 2)
+ return;
+
+ ti = &tlv->ti;
+
+ log_debug ("p12_parse:%s:%d: @%04zu class=%d tag=%lu len=%zu nhdr=%zu %s%s\n",
+ text, lno,
+ (size_t)(tlv->buffer - tlv->origbuffer) - ti->nhdr,
+ ti->class, ti->tag, ti->length, ti->nhdr,
+ ti->is_constructed?" cons":"",
+ ti->ndef?" ndef":"");
+}
+
+
+void
+_tlv_parser_dump_state (const char *text, const char *text2,
+ int lno, tlv_parser_t tlv)
+{
+ if (!tlv || tlv->verbosity < 2)
+ return;
+
+ log_debug ("p12_parse:%s%s%s:%d: @%04zu lvl=%u %s\n",
+ text,
+ text2? "/":"", text2? text2:"",
+ lno,
+ (size_t)(tlv->buffer - tlv->origbuffer),
+ tlv->stacklen,
+ tlv->in_ndef? " in-ndef":"");
+}
+
+
+
+/* Parse the buffer at the address BUFFER which is of SIZE and return
+ * the tag and the length part from the TLV triplet. Update BUFFER
+ * and SIZE on success. Checks that the encoded length does not
+ * exhaust the length of the provided buffer. */
+static int
+parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
+{
+ gpg_error_t err;
+ int tag;
+
+ err = parse_ber_header (buffer, size,
+ &ti->class, &tag,
+ &ti->is_constructed, &ti->ndef,
+ &ti->length, &ti->nhdr);
+ if (err)
+ return err;
+ if (tag < 0)
+ return gpg_error (GPG_ERR_EOVERFLOW);
+ ti->tag = tag;
+
+ if (ti->length > *size)
+ return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); /* data larger than buffer. */
+
+ return 0;
+}
+
+/* Public version of parse_tag. */
+gpg_error_t
+tlv_parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
+{
+ return parse_tag (buffer, size, ti);
+}
+
+
+/* Create a new TLV object. */
+tlv_parser_t
+tlv_parser_new (const unsigned char *buffer, size_t bufsize, int verbosity)
+{
+ tlv_parser_t tlv;
+ tlv = xtrycalloc (1, sizeof *tlv);
+ if (tlv)
+ {
+ tlv->origbuffer = buffer;
+ tlv->origbufsize = bufsize;
+ tlv->buffer = buffer;
+ tlv->bufsize = bufsize;
+ tlv->verbosity = verbosity;
+ }
+ return tlv;
+}
+
+
+/* This function can be used to store a malloced buffer into the TLV
+ * object. Ownership of BUFFER is thus transferred to TLV. This
+ * buffer will then only be released by tlv_release. */
+static gpg_error_t
+register_buffer (tlv_parser_t tlv, char *buffer)
+{
+ struct bufferlist_s *item;
+
+ item = xtrycalloc (1, sizeof *item);
+ if (!item)
+ return gpg_error_from_syserror ();
+ item->buffer = buffer;
+ item->next = tlv->bufferlist;
+ tlv->bufferlist = item;
+ return 0;
+}
+
+
+void
+tlv_parser_release (tlv_parser_t tlv)
+{
+ if (!tlv)
+ return;
+ while (tlv->bufferlist)
+ {
+ struct bufferlist_s *save = tlv->bufferlist->next;
+ xfree (tlv->bufferlist->buffer);
+ xfree (tlv->bufferlist);
+ tlv->bufferlist = save;
+ }
+ xfree (tlv);
+}
+
+
+/* Helper for the tlv_peek functions. */
+static gpg_error_t
+_tlv_peek (tlv_parser_t tlv, struct tag_info *ti)
+{
+ const unsigned char *p;
+ size_t n;
+
+ /* Note that we want to peek ahead of any current container but of
+ * course not beyond our entire buffer. */
+ p = tlv->buffer;
+ if ((p - tlv->origbuffer) > tlv->origbufsize)
+ return gpg_error (GPG_ERR_BUG);
+ n = tlv->origbufsize - (p - tlv->origbuffer);
+ return parse_tag (&p, &n, ti);
+}
+
+
+/* Look for the next tag and return true if it matches CLASS and TAG.
+ * Otherwise return false. No state is changed. */
+int
+_tlv_parser_peek (tlv_parser_t tlv, int class, int tag)
+{
+ struct tag_info ti;
+
+ return (!_tlv_peek (tlv, &ti)
+ && ti.class == class && ti.tag == tag);
+}
+
+
+/* Look for the next tag and return true if it is the Null tag.
+ * Otherwise return false. No state is changed. */
+int
+_tlv_parser_peek_null (tlv_parser_t tlv)
+{
+ struct tag_info ti;
+
+ return (!_tlv_peek (tlv, &ti)
+ && ti.class == CLASS_UNIVERSAL && ti.tag == TAG_NULL
+ && !ti.is_constructed && !ti.length);
+}
+
+
+/* Helper for tlv_expect_sequence and tlv_expect_context_tag. */
+static gpg_error_t
+_tlv_push (tlv_parser_t tlv)
+{
+ /* Right now our pointer is at the value of the current container.
+ * We push that info onto the stack. */
+ if (tlv->stacklen >= TLV_MAX_DEPTH)
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_MANY));
+ tlv->stack[tlv->stacklen].buffer = tlv->buffer;
+ tlv->stack[tlv->stacklen].bufsize = tlv->bufsize;
+ tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef;
+ tlv->stack[tlv->stacklen].length = tlv->ti.length;
+ tlv->stacklen++;
+
+ tlv->in_ndef = tlv->ti.ndef;
+
+ /* We set the size of the buffer to the TLV length if it is known or
+ * else to the size of the remaining entire buffer. */
+ if (tlv->in_ndef)
+ {
+ if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize)
+ return (tlv->lasterr = gpg_error (GPG_ERR_BUG));
+ tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer);
+ }
+ else
+ tlv->bufsize = tlv->ti.length;
+
+ _tlv_parser_dump_state (__func__, NULL, 0, tlv);
+ return 0;
+}
+
+
+/* Helper for tlv_next. */
+static gpg_error_t
+_tlv_pop (tlv_parser_t tlv)
+{
+ size_t lastlen;
+
+ /* We reached the end of a container, either due to the size limit
+ * or due to an end tag. Now we pop the last container so that we
+ * are positioned at the value of the last container. */
+ if (!tlv->stacklen)
+ return gpg_error (GPG_ERR_EOF);
+
+ tlv->stacklen--;
+ tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef;
+ if (tlv->in_ndef)
+ {
+ /* We keep buffer but adjust bufsize to the end of the origbuffer. */
+ if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize)
+ return (tlv->lasterr = gpg_error (GPG_ERR_BUG));
+ tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer);
+ }
+ else
+ {
+ lastlen = tlv->stack[tlv->stacklen].length;
+ tlv->buffer = tlv->stack[tlv->stacklen].buffer;
+ tlv->bufsize = tlv->stack[tlv->stacklen].bufsize;
+ if (lastlen > tlv->bufsize)
+ {
+ log_debug ("%s: container length larger than buffer (%zu/%zu)\n",
+ __func__, lastlen, tlv->bufsize);
+ return gpg_error (GPG_ERR_INV_BER);
+ }
+ tlv->buffer += lastlen;
+ tlv->bufsize -= lastlen;
+ }
+
+ _tlv_parser_dump_state (__func__, NULL, 0, tlv);
+ return 0;
+}
+
+
+/* Parse the next tag and value. Also detect the end of a
+ * container. The caller should use the tlv_next macro. */
+gpg_error_t
+_tlv_parser_next (tlv_parser_t tlv, int lno)
+{
+ gpg_error_t err;
+
+ tlv->lasterr = 0;
+ tlv->lastfunc = __func__;
+
+ if (tlv->pending)
+ {
+ tlv->pending = 0;
+ if (tlv->verbosity > 1)
+ log_debug ("%s: skipped\n", __func__);
+ return 0;
+ }
+
+ if (tlv->verbosity > 1)
+ log_debug ("%s: called\n", __func__);
+ /* If we are at the end of an ndef container pop the stack. */
+ if (!tlv->in_ndef && !tlv->bufsize)
+ {
+ do
+ err = _tlv_pop (tlv);
+ while (!err && !tlv->in_ndef && !tlv->bufsize);
+ if (err)
+ return (tlv->lasterr = err);
+ if (tlv->verbosity > 1)
+ log_debug ("%s: container(s) closed due to size\n", __func__);
+ }
+
+ again:
+ /* Get the next tag. */
+ err = parse_tag (&tlv->buffer, &tlv->bufsize, &tlv->ti);
+ if (err)
+ {
+ if (tlv->verbosity > 1)
+ log_debug ("%s: reading tag returned err=%d\n", __func__, err);
+ return err;
+ }
+
+ /* If there is an end tag in an ndef container pop the stack. Also
+ * pop other containers which are fully consumed. */
+ if (tlv->in_ndef && (tlv->ti.class == CLASS_UNIVERSAL
+ && !tlv->ti.tag && !tlv->ti.is_constructed))
+ {
+ do
+ err = _tlv_pop (tlv);
+ while (!err && !tlv->in_ndef && !tlv->bufsize);
+ if (err)
+ return (tlv->lasterr = err);
+ if (tlv->verbosity > 1)
+ log_debug ("%s: container(s) closed due to end tag\n", __func__);
+ goto again;
+ }
+
+ _tlv_parser_dump_tag (__func__, lno, tlv);
+ return 0;
+}
+
+
+/* Return the current neting level of the TLV object. */
+unsigned int
+tlv_parser_level (tlv_parser_t tlv)
+{
+ return tlv? tlv->stacklen : 0;
+}
+
+/* Returns the current offset of the parser. */
+size_t
+tlv_parser_offset (tlv_parser_t tlv)
+{
+ return tlv? (size_t)(tlv->buffer - tlv->origbuffer) : 0;
+}
+
+
+/* Return a string with the last function used. If TLV is NULL an
+ * empty string is returned. */
+const char *
+tlv_parser_lastfunc (tlv_parser_t tlv)
+{
+ return tlv? tlv->lastfunc:"";
+}
+
+
+const char *
+tlv_parser_lasterrstr (tlv_parser_t tlv)
+{
+ return tlv? gpg_strerror (tlv->lasterr) : "tlv parser not yet initialized";
+}
+
+
+/* Set a flag to indicate that the last tlv_next has not yet been
+ * consumed. */
+void
+tlv_parser_set_pending (tlv_parser_t tlv)
+{
+ tlv->pending = 1;
+}
+
+
+/* Return the length of the last read tag. If with_header is 1 the
+ * lengtb of the header is added to the returned length. */
+size_t
+tlv_parser_tag_length (tlv_parser_t tlv, int with_header)
+{
+ if (with_header)
+ return tlv->ti.length + tlv->ti.nhdr;
+ else
+ return tlv->ti.length;
+}
+
+
+/* Skip over the value of the current tag. Does not yet work for ndef
+ * containers. */
+void
+tlv_parser_skip (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ log_assert (tlv->bufsize >= tlv->ti.length);
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+}
+
+
+/* Expect that the current tag is a sequence and setup the context for
+ * processing. */
+gpg_error_t
+tlv_expect_sequence (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE
+ && tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ return _tlv_push (tlv);
+}
+
+
+/* Expect that the current tag is a context tag and setup the context
+ * for processing. The tag of the context is returned at R_TAG. */
+gpg_error_t
+tlv_expect_context_tag (tlv_parser_t tlv, int *r_tag)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_CONTEXT && tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ *r_tag = tlv->ti.tag;
+ return _tlv_push (tlv);
+}
+
+
+/* Expect that the current tag is a SET and setup the context for
+ * processing. */
+gpg_error_t
+tlv_expect_set (tlv_parser_t tlv)
+{
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SET
+ && tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ return _tlv_push (tlv);
+}
+
+
+/* Expect an object of CLASS with TAG and store its value at
+ * (R_DATA,R_DATALEN). Then skip over its value to the next tag.
+ * Note that the stored value is not allocated but points into
+ * TLV. */
+gpg_error_t
+tlv_expect_object (tlv_parser_t tlv, int class, int tag,
+ unsigned char const **r_data, size_t *r_datalen)
+{
+ gpg_error_t err;
+ const unsigned char *p;
+ size_t n;
+ int needpush = 0;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == class && tlv->ti.tag == tag))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ n = tlv->ti.length;
+ if (!n && tlv->ti.ndef)
+ {
+ n = tlv->bufsize;
+ needpush = 1;
+ }
+ else if (!tlv->ti.length)
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (class == CLASS_CONTEXT && tag == 0 && tlv->ti.is_constructed
+ && need_octet_string_cramming (p, n))
+ {
+ char *newbuffer;
+
+ newbuffer = cram_octet_string (p, n, r_datalen);
+ if (!newbuffer)
+ return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER));
+ err = register_buffer (tlv, newbuffer);
+ if (err)
+ {
+ xfree (newbuffer);
+ return (tlv->lasterr = err);
+ }
+ *r_data = newbuffer;
+ }
+ else
+ {
+ *r_data = p;
+ *r_datalen = n;
+ }
+ if (needpush)
+ return _tlv_push (tlv);
+
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ return 0;
+}
+
+
+/* Expect that the current tag is an object string and store its value
+ * at (R_DATA,R_DATALEN). Then skip over its value to the next tag.
+ * Note that the stored value are not allocated but point into TLV.
+ * If ENCAPSULATES is set the octet string is used as a new
+ * container. R_DATA and R_DATALEN are optional. */
+gpg_error_t
+tlv_expect_octet_string (tlv_parser_t tlv, int encapsulates,
+ unsigned char const **r_data, size_t *r_datalen)
+{
+ gpg_error_t err;
+ const unsigned char *p;
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING
+ && (!tlv->ti.is_constructed || encapsulates)))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length) && !tlv->ti.ndef)
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (encapsulates && tlv->ti.is_constructed
+ && need_octet_string_cramming (p, n))
+ {
+ char *newbuffer;
+
+ newbuffer = cram_octet_string (p, n, r_datalen);
+ if (!newbuffer)
+ return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER));
+ err = register_buffer (tlv, newbuffer);
+ if (err)
+ {
+ xfree (newbuffer);
+ return (tlv->lasterr = err);
+ }
+ *r_data = newbuffer;
+ }
+ else
+ {
+ if (r_data)
+ *r_data = p;
+ if (r_datalen)
+ *r_datalen = tlv->ti.length;
+ }
+ if (encapsulates)
+ return _tlv_push (tlv);
+
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ return 0;
+}
+
+
+/* Expect that the current tag is an integer and return its value at
+ * R_VALUE. Then skip over its value to the next tag. */
+gpg_error_t
+tlv_expect_integer (tlv_parser_t tlv, int *r_value)
+{
+ const unsigned char *p;
+ size_t n;
+ int value;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ /* We currently support only positive values. */
+ if ((*p & 0x80))
+ return (tlv->lasterr = gpg_error (GPG_ERR_ERANGE));
+
+ for (value = 0; n; n--)
+ {
+ value <<= 8;
+ value |= (*p++) & 0xff;
+ if (value < 0)
+ return (tlv->lasterr = gpg_error (GPG_ERR_EOVERFLOW));
+ }
+ *r_value = value;
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ return 0;
+}
+
+
+/* Variant of tlv_expect_integer which returns an MPI. If IGNORE_ZERO
+ * is set a value of 0 is ignored and R_VALUE not changed and the
+ * function returns GPG_ERR_FALSE. No check for negative encoded
+ * integers is done because the old code here worked the same and we
+ * can't foreclose invalid encoded PKCS#12 stuff - after all it is
+ * PKCS#12 see https://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html */
+#ifdef GCRYPT_VERSION
+gpg_error_t
+tlv_expect_mpinteger (tlv_parser_t tlv, int ignore_zero,
+ gcry_mpi_t *r_value)
+{
+ const unsigned char *p;
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ if (ignore_zero && n == 1 && !*p)
+ return gpg_error (GPG_ERR_FALSE);
+
+ return gcry_mpi_scan (r_value, GCRYMPI_FMT_USG, p, n, NULL);
+}
+#endif /*GCRYPT_VERSION*/
+
+
+/* Expect that the current tag is an object id and store its value at
+ * (R_OID,R_OIDLEN). Then skip over its value to the next tag. Note
+ * that the stored value is not allocated but points into TLV. */
+gpg_error_t
+tlv_expect_object_id (tlv_parser_t tlv,
+ unsigned char const **r_oid, size_t *r_oidlen)
+{
+ const unsigned char *p;
+ size_t n;
+
+ tlv->lastfunc = __func__;
+ if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OBJECT_ID
+ && !tlv->ti.is_constructed))
+ return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
+ p = tlv->buffer;
+ if (!(n=tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+
+ *r_oid = p;
+ *r_oidlen = tlv->ti.length;
+ if (!(tlv->bufsize >= tlv->ti.length))
+ return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
+ tlv->buffer += tlv->ti.length;
+ tlv->bufsize -= tlv->ti.length;
+ return 0;
+}
+
+
+/* Given an ASN.1 chunk of a structure like:
+ *
+ * 24 NDEF: OCTET STRING -- This is not passed to us
+ * 04 1: OCTET STRING -- INPUT point s to here
+ * : 30
+ * 04 1: OCTET STRING
+ * : 80
+ * [...]
+ * 04 2: OCTET STRING
+ * : 00 00
+ * : } -- This denotes a Null tag and are the last
+ * -- two bytes in INPUT.
+ *
+ * The example is from Mozilla Firefox 1.0.4 which actually exports
+ * certs as single byte chunks of octet strings.
+ *
+ * Create a new buffer with the content of that octet string. INPUT
+ * is the original buffer with a LENGTH. Returns
+ * NULL on error or a new malloced buffer with its actual used length
+ * stored at R_NEWLENGTH. */
+static unsigned char *
+cram_octet_string (const unsigned char *input, size_t length,
+ size_t *r_newlength)
+{
+ const unsigned char *s = input;
+ size_t n = length;
+ unsigned char *output, *d;
+ struct tag_info ti;
+
+ /* Allocate output buf. We know that it won't be longer than the
+ input buffer. */
+ d = output = xtrymalloc (length);
+ if (!output)
+ goto bailout;
+
+ while (n)
+ {
+ if (parse_tag (&s, &n, &ti))
+ goto bailout;
+ if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING
+ && !ti.ndef && !ti.is_constructed)
+ {
+ memcpy (d, s, ti.length);
+ s += ti.length;
+ d += ti.length;
+ n -= ti.length;
+ }
+ else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
+ break; /* Ready */
+ else
+ goto bailout;
+ }
+
+
+ *r_newlength = d - output;
+ return output;
+
+ bailout:
+ xfree (output);
+ return NULL;
+}
+
+
+/* Return true if (INPUT,LENGTH) is a structure which should be passed
+ * to cram_octet_string. This is basically the same loop as in
+ * cram_octet_string but without any actual copying. */
+static int
+need_octet_string_cramming (const unsigned char *input, size_t length)
+{
+ const unsigned char *s = input;
+ size_t n = length;
+ struct tag_info ti;
+
+ if (!length)
+ return 0;
+
+ while (n)
+ {
+ if (parse_tag (&s, &n, &ti))
+ return 0;
+ if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING
+ && !ti.ndef && !ti.is_constructed)
+ {
+ s += ti.length;
+ n -= ti.length;
+ }
+ else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
+ break; /* Ready */
+ else
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/common/tlv.h b/common/tlv.h
index e371ca57e..afaa649d9 100644
--- a/common/tlv.h
+++ b/common/tlv.h
@@ -71,10 +71,22 @@ enum tlv_tag_type {
TAG_BMP_STRING = 30
};
+struct tag_info
+{
+ int class;
+ int is_constructed;
+ unsigned long tag;
+ size_t length; /* length part of the TLV */
+ size_t nhdr;
+ int ndef; /* It is an indefinite length */
+};
struct tlv_builder_s;
typedef struct tlv_builder_s *tlv_builder_t;
+struct tlv_parser_s;
+typedef struct tlv_parser_s *tlv_parser_t;
+
/*-- tlv.c --*/
/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
@@ -94,7 +106,7 @@ const unsigned char *find_tlv_unchecked (const unsigned char *buffer,
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
- on success. */
+ on success. See also tlv_parse_tag. */
gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed,
@@ -137,6 +149,59 @@ void put_tlv_to_membuf (membuf_t *membuf, int class, int tag,
size_t get_tlv_length (int class, int tag, int constructed, size_t length);
+/*-- tlv-parser.c --*/
+
+tlv_parser_t tlv_parser_new (const unsigned char *buffer, size_t bufsize,
+ int verbosity);
+void tlv_parser_release (tlv_parser_t tlv);
+
+void _tlv_parser_dump_tag (const char *text, int lno, tlv_parser_t tlv);
+void _tlv_parser_dump_state (const char *text, const char *text2,
+ int lno, tlv_parser_t tlv);
+
+int _tlv_parser_peek (tlv_parser_t tlv, int class, int tag);
+int _tlv_parser_peek_null (tlv_parser_t tlv);
+gpg_error_t _tlv_parser_next (tlv_parser_t tlv, int lno);
+
+unsigned int tlv_parser_level (tlv_parser_t tlv);
+size_t tlv_parser_offset (tlv_parser_t tlv);
+const char *tlv_parser_lastfunc (tlv_parser_t tlv);
+const char *tlv_parser_lasterrstr (tlv_parser_t tlv);
+void tlv_parser_set_pending (tlv_parser_t tlv);
+size_t tlv_parser_tag_length (tlv_parser_t tlv, int with_header);
+
+void tlv_parser_skip (tlv_parser_t tlv);
+
+gpg_error_t tlv_expect_sequence (tlv_parser_t tlv);
+gpg_error_t tlv_expect_context_tag (tlv_parser_t tlv, int *r_tag);
+gpg_error_t tlv_expect_set (tlv_parser_t tlv);
+gpg_error_t tlv_expect_object (tlv_parser_t tlv, int class, int tag,
+ unsigned char const **r_data,
+ size_t *r_datalen);
+gpg_error_t tlv_expect_octet_string (tlv_parser_t tlv, int encapsulates,
+ unsigned char const **r_data,
+ size_t *r_datalen);
+gpg_error_t tlv_expect_integer (tlv_parser_t tlv, int *r_value);
+#ifdef GCRYPT_VERSION
+gpg_error_t tlv_expect_mpinteger (tlv_parser_t tlv, int ignore_zero,
+ gcry_mpi_t *r_value);
+#endif
+gpg_error_t tlv_expect_object_id (tlv_parser_t tlv,
+ unsigned char const **r_oid,
+ size_t *r_oidlen);
+
+/* Easier to use wrapper around parse_ber_header. */
+gpg_error_t tlv_parse_tag (unsigned char const **buffer,
+ size_t *size, struct tag_info *ti);
+
+/* Convenience macro and macros to include the line number. */
+#define tlv_parser_dump_tag(a,b) _tlv_parser_dump_tag ((a),__LINE__,(b))
+#define tlv_parser_dump_state(a,b,c) \
+ _tlv_parser_dump_state ((a),(b),__LINE__,(c))
+#define tlv_peek(a,b,c) _tlv_parser_peek ((a),(b),(c))
+#define tlv_peek_null(a) _tlv_parser_peek_null ((a))
+#define tlv_next(a) _tlv_parser_next ((a), __LINE__)
+
#endif /* SCD_TLV_H */
diff --git a/common/util.h b/common/util.h
index 764030ad8..2b46ec930 100644
--- a/common/util.h
+++ b/common/util.h
@@ -39,6 +39,11 @@
* libgpg-error version. Define them here.
* Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21)
*/
+#if GPG_ERROR_VERSION_NUMBER < 0x012f00 /* 1.47 */
+# define GPG_ERR_BAD_PUK 320
+# define GPG_ERR_NO_RESET_CODE 321
+# define GPG_ERR_BAD_RESET_CODE 322
+#endif
#ifndef EXTERN_UNLESS_MAIN_MODULE
# if !defined (INCLUDED_BY_MAIN_MODULE)
@@ -143,6 +148,7 @@ ssize_t read_line (FILE *fp,
char **addr_of_buffer, size_t *length_of_buffer,
size_t *max_length);
+
/*-- sexputil.c */
char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen);
void log_printcanon (const char *text,
@@ -352,6 +358,10 @@ struct compatibility_flags_s
int parse_compatibility_flags (const char *string, unsigned int *flagvar,
const struct compatibility_flags_s *flags);
+gpg_error_t b64decode (const char *string, const char *title,
+ void **r_buffer, size_t *r_buflen);
+
+
/*-- Simple replacement functions. */
diff --git a/configure.ac b/configure.ac
index 299d39664..c21138641 100644
--- a/configure.ac
+++ b/configure.ac
@@ -67,13 +67,13 @@ NEED_KSBA_API=1
NEED_KSBA_VERSION=1.6.3
NEED_NTBTLS_API=1
-NEED_NTBTLS_VERSION=0.1.0
+NEED_NTBTLS_VERSION=0.2.0
NEED_NPTH_API=1
NEED_NPTH_VERSION=1.2
-NEED_GNUTLS_VERSION=3.0
+NEED_GNUTLS_VERSION=3.2
NEED_SQLITE_VERSION=3.27
diff --git a/dirmngr/Makefile.am b/dirmngr/Makefile.am
index 3846fdf35..a0f8d5a79 100644
--- a/dirmngr/Makefile.am
+++ b/dirmngr/Makefile.am
@@ -68,6 +68,7 @@ AM_CFLAGS = $(USE_C99_CFLAGS) \
if HAVE_W32_SYSTEM
ldap_url = ldap-url.h ldap-url.c
+NETLIBS += -lwinhttp -lsecurity
else
ldap_url =
endif
diff --git a/dirmngr/dirmngr.c b/dirmngr/dirmngr.c
index 4619b0d7e..d58a27372 100644
--- a/dirmngr/dirmngr.c
+++ b/dirmngr/dirmngr.c
@@ -2046,6 +2046,7 @@ dirmngr_sighup_action (void)
crl_cache_deinit ();
cert_cache_init (hkp_cacert_filenames);
crl_cache_init ();
+ http_reinitialize ();
reload_dns_stuff (0);
ks_hkp_reload ();
}
diff --git a/dirmngr/http-common.h b/dirmngr/http-common.h
index 5e6657b16..ddb340de6 100644
--- a/dirmngr/http-common.h
+++ b/dirmngr/http-common.h
@@ -22,4 +22,6 @@
const char *get_default_keyserver (int name_only);
+void http_reinitialize (void);
+
#endif /* HTTP_COMMON_H */
diff --git a/dirmngr/http.c b/dirmngr/http.c
index a7fd6ec26..ea9b0365b 100644
--- a/dirmngr/http.c
+++ b/dirmngr/http.c
@@ -2,7 +2,7 @@
* Copyright (C) 1999, 2001-2004, 2006, 2009, 2010,
* 2011 Free Software Foundation, Inc.
* Copyright (C) 1999, 2001-2004, 2006, 2009, 2010, 2011, 2014 Werner Koch
- * Copyright (C) 2015-2017, 2021 g10 Code GmbH
+ * Copyright (C) 2015-2017, 2021, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -64,12 +64,9 @@
# include
# endif
# include
-# ifndef EHOSTUNREACH
-# define EHOSTUNREACH WSAEHOSTUNREACH
-# endif
-# ifndef EAFNOSUPPORT
-# define EAFNOSUPPORT WSAEAFNOSUPPORT
-# endif
+# include
+# define SECURITY_WIN32 1
+# include
#else /*!HAVE_W32_SYSTEM*/
# include
# include
@@ -210,10 +207,29 @@ struct cookie_s
/* True if TLS is to be used. */
int use_tls;
+ /* Optional malloced buffer holding pending bytes for the read
+ * function. LEN gives the used length, SIZE the allocated length.
+ * Used by the up_to_empty_line machinery. */
+ struct {
+ size_t size;
+ size_t len;
+ char *data;
+ } pending;
+
/* The remaining content length and a flag telling whether to use
the content length. */
uint64_t content_length;
unsigned int content_length_valid:1;
+
+ /* If the next flag is set the read function will limit the returned
+ * buffer to an empty line. That is the the pattern "\n\r\n" is
+ * detected and any further bytes are not returned to the caller.
+ * The flag is then reset. For technical reason we might have
+ * already read more which will be then saved for the next call in
+ * the PENDING buffer. */
+ unsigned int up_to_empty_line:1;
+ unsigned int last_was_lf:1; /* Helper to detect empty line. */
+ unsigned int last_was_lfcr:1; /* Helper to detect empty line. */
};
typedef struct cookie_s *cookie_t;
@@ -230,6 +246,33 @@ static es_cookie_io_functions_t simple_cookie_functions =
};
#endif
+enum auth_negotiate_states
+ {
+ AUTH_NGT_NONE = 0,
+ AUTH_NGT_RCVD = 1,
+ AUTH_NGT_SENT = 2
+ };
+
+/* An object to store information about a proxy. */
+struct proxy_info_s
+{
+ parsed_uri_t uri; /* The parsed proxy URL. */
+ int is_http_proxy; /* This is an http proxy. */
+
+#ifdef HAVE_W32_SYSTEM
+ CredHandle cred_handle; /* Credential handle. */
+ wchar_t *spn; /* Service principal name. */
+ CtxtHandle ctxt_handle; /* Security context. */
+ unsigned long token_size; /* Max. length of a token. */
+ unsigned int cred_handle_valid:1;
+ unsigned int ctxt_handle_valid:1;
+#endif /*HAVE_W32_SYSTEM*/
+
+ unsigned char *outtoken; /* The output token allocated with token_size. */
+ unsigned long outtoklen; /* The current length of the token. */
+};
+typedef struct proxy_info_s *proxy_info_t;
+
#if SIZEOF_UNSIGNED_LONG == 8
# define HTTP_SESSION_MAGIC 0x0068545470534553 /* "hTTpSES" */
@@ -297,6 +340,7 @@ struct http_context_s
my_socket_t sock;
unsigned int in_data:1;
unsigned int is_http_0_9:1;
+ unsigned int keep_alive:1; /* Keep the connection alive. */
estream_t fp_read;
estream_t fp_write;
void *write_cookie;
@@ -317,13 +361,13 @@ struct http_context_s
static int opt_verbose;
static int opt_debug;
-/* The global callback for the verification function. */
+/* The global callback for the verification function for GNUTLS. */
static gpg_error_t (*tls_callback) (http_t, http_session_t, int);
-/* The list of files with trusted CA certificates. */
+/* The list of files with trusted CA certificates for GNUTLS. */
static strlist_t tls_ca_certlist;
-/* The list of files with extra trusted CA certificates. */
+/* The list of files with extra trusted CA certificates for GNUTLS. */
static strlist_t cfg_ca_certlist;
/* The global callback for net activity. */
@@ -596,7 +640,7 @@ http_set_verbose (int verbose, int debug)
/* Register a non-standard global TLS callback function. If no
verification is desired a callback needs to be registered which
- always returns NULL. */
+ always returns NULL. Only used for GNUTLS. */
void
http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int))
{
@@ -607,7 +651,7 @@ http_register_tls_callback (gpg_error_t (*cb)(http_t, http_session_t, int))
/* Register a CA certificate for future use. The certificate is
expected to be in FNAME. PEM format is assume if FNAME has a
suffix of ".pem". If FNAME is NULL the list of CA files is
- removed. */
+ removed. Only used for GNUTLS. */
void
http_register_tls_ca (const char *fname)
{
@@ -636,7 +680,8 @@ http_register_tls_ca (const char *fname)
* expected to be in FNAME. PEM format is assume if FNAME has a
* suffix of ".pem". If FNAME is NULL the list of CA files is
* removed. This is a variant of http_register_tls_ca which puts the
- * certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG. */
+ * certificate into a separate list enabled using HTTP_FLAG_TRUST_CFG.
+ * Only used for GNUTLS. */
void
http_register_cfg_ca (const char *fname)
{
@@ -737,6 +782,64 @@ http_session_release (http_session_t sess)
}
+/* Create a write stream and store it in the fp_write member. Also
+ * store the tls flag and the session. */
+static gpg_error_t
+make_fp_write (http_t hd, int use_tls, http_session_t session)
+{
+ cookie_t cookie;
+
+ cookie = xtrycalloc (1, sizeof *cookie);
+ if (!cookie)
+ return gpg_error_from_syserror ();
+ cookie->sock = my_socket_ref (hd->sock);
+ cookie->use_tls = use_tls;
+ if (session)
+ cookie->session = http_session_ref (session);
+ hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
+ if (!hd->fp_write)
+ {
+ gpg_error_t err = gpg_error_from_syserror ();
+ my_socket_unref (cookie->sock, NULL, NULL);
+ if (session)
+ http_session_unref (cookie->session);
+ xfree (cookie);
+ return err;
+ }
+ hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE. */
+ return 0;
+}
+
+
+/* Create a read stream and store it in the fp_read member. Also
+ * store the tls flag and the session. */
+static gpg_error_t
+make_fp_read (http_t hd, int use_tls, http_session_t session)
+{
+ cookie_t cookie;
+
+ cookie = xtrycalloc (1, sizeof *cookie);
+ if (!cookie)
+ return gpg_error_from_syserror ();
+ cookie->sock = my_socket_ref (hd->sock);
+ cookie->use_tls = use_tls;
+ if (session)
+ cookie->session = http_session_ref (session);
+ hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
+ if (!hd->fp_read)
+ {
+ gpg_error_t err = gpg_error_from_syserror ();
+ my_socket_unref (cookie->sock, NULL, NULL);
+ if (session)
+ http_session_unref (cookie->session);
+ xfree (cookie);
+ return err;
+ }
+ hd->read_cookie = cookie; /* Cookie now owned by FP_READ. */
+ return 0;
+}
+
+
/* Create a new session object which is currently used to enable TLS
* support. It may eventually allow reusing existing connections.
* Valid values for FLAGS are:
@@ -786,6 +889,8 @@ http_session_new (http_session_t *r_session,
int add_system_cas = !!(flags & HTTP_FLAG_TRUST_SYS);
int is_hkps_pool;
+ (void)intended_hostname;
+
rc = gnutls_certificate_allocate_credentials (&sess->certcred);
if (rc < 0)
{
@@ -850,7 +955,6 @@ http_session_new (http_session_t *r_session,
/* Add system certificates to the session. */
if (add_system_cas)
{
-#if GNUTLS_VERSION_NUMBER >= 0x030014
static int shown;
rc = gnutls_certificate_set_x509_system_trust (sess->certcred);
@@ -861,7 +965,6 @@ http_session_new (http_session_t *r_session,
shown = 1;
log_info ("number of system provided CAs: %d\n", rc);
}
-#endif /* gnutls >= 3.0.20 */
}
/* Add other configured certificates to the session. */
@@ -1027,7 +1130,6 @@ http_raw_connect (ctrl_t ctrl, http_t *r_hd,
{
gpg_error_t err = 0;
http_t hd;
- cookie_t cookie;
*r_hd = NULL;
@@ -1075,39 +1177,13 @@ http_raw_connect (ctrl_t ctrl, http_t *r_hd,
}
/* Setup estreams for reading and writing. */
- cookie = xtrycalloc (1, sizeof *cookie);
- if (!cookie)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- goto leave;
- }
- cookie->sock = my_socket_ref (hd->sock);
- hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
- if (!hd->fp_write)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- my_socket_unref (cookie->sock, NULL, NULL);
- xfree (cookie);
- goto leave;
- }
- hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE. */
+ err = make_fp_write (hd, 0, NULL);
+ if (err)
+ goto leave;
- cookie = xtrycalloc (1, sizeof *cookie);
- if (!cookie)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- goto leave;
- }
- cookie->sock = my_socket_ref (hd->sock);
- hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
- if (!hd->fp_read)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- my_socket_unref (cookie->sock, NULL, NULL);
- xfree (cookie);
- goto leave;
- }
- hd->read_cookie = cookie; /* Cookie now owned by FP_READ. */
+ err = make_fp_read (hd, 0, NULL);
+ if (err)
+ goto leave;
/* Register close notification to interlock the use of es_fclose in
http_close and in user code. */
@@ -1139,7 +1215,7 @@ http_start_data (http_t hd)
if (!hd->in_data)
{
if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
- log_debug_string ("\r\n", "http.c:request-header:");
+ log_debug ("http.c:request-header:start_data:\n");
es_fputs ("\r\n", hd->fp_write);
es_fflush (hd->fp_write);
hd->in_data = 1;
@@ -1155,6 +1231,7 @@ http_wait_response (http_t hd)
gpg_error_t err;
cookie_t cookie;
int use_tls;
+ int newfpread;
/* Make sure that we are in the data. */
http_start_data (hd);
@@ -1166,41 +1243,36 @@ http_wait_response (http_t hd)
return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
use_tls = cookie->use_tls;
- es_fclose (hd->fp_write);
- hd->fp_write = NULL;
- /* The close has released the cookie and thus we better set it to NULL. */
- hd->write_cookie = NULL;
+ if (!hd->keep_alive)
+ {
+ es_fclose (hd->fp_write);
+ hd->fp_write = NULL;
+ /* The close has released the cookie and thus we better set it
+ * to NULL. */
+ hd->write_cookie = NULL;
+ }
/* Shutdown one end of the socket is desired. As per HTTP/1.0 this
is not required but some very old servers (e.g. the original pksd
keyserver didn't worked without it. */
- if ((hd->flags & HTTP_FLAG_SHUTDOWN))
+ if (!hd->keep_alive && (hd->flags & HTTP_FLAG_SHUTDOWN))
shutdown (FD2INT (hd->sock->fd), 1);
hd->in_data = 0;
/* Create a new cookie and a stream for reading. */
- cookie = xtrycalloc (1, sizeof *cookie);
- if (!cookie)
- return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- cookie->sock = my_socket_ref (hd->sock);
- cookie->session = http_session_ref (hd->session);
- cookie->use_tls = use_tls;
-
- hd->read_cookie = cookie;
- hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
- if (!hd->fp_read)
+ newfpread = 0;
+ if (!hd->keep_alive || !hd->fp_read)
{
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- my_socket_unref (cookie->sock, NULL, NULL);
- http_session_unref (cookie->session);
- xfree (cookie);
- hd->read_cookie = NULL;
- return err;
+ err = make_fp_read (hd, use_tls, hd->session);
+ if (err)
+ return err;
+ newfpread = 1;
+ ((cookie_t)(hd->read_cookie))->up_to_empty_line = 1;
}
err = parse_response (hd);
- if (!err)
+ if (!err && newfpread)
err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
return err;
@@ -1789,34 +1861,266 @@ is_hostname_port (const char *string)
}
-/*
- * Send a HTTP request to the server
- * Returns 0 if the request was successful
- */
-static gpg_error_t
-send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
- const char *proxy, const char *srvtag, unsigned int timeout,
- strlist_t headers)
+/* Free the PROXY object. */
+static void
+release_proxy_info (proxy_info_t proxy)
{
- gpg_error_t err;
- const char *server;
- char *request, *p;
- unsigned short port;
- const char *http_proxy = NULL;
- char *proxy_authstr = NULL;
- char *authstr = NULL;
- assuan_fd_t sock;
- int have_http_proxy = 0;
+ if (!proxy)
+ return;
+ http_release_parsed_uri (proxy->uri);
+ xfree (proxy->outtoken);
+#ifdef HAVE_W32_SYSTEM
+ if (proxy->ctxt_handle_valid)
+ DeleteSecurityContext (&proxy->ctxt_handle);
+ if (proxy->cred_handle_valid)
+ FreeCredentialsHandle (&proxy->cred_handle);
+#endif
+ xfree (proxy);
+}
+
+
+/* Return an http session object. If clear is set, the object is
+ * destroyed. On error nULL is returned. */
+#ifdef HAVE_W32_SYSTEM
+static HINTERNET
+w32_get_internet_session (int clear)
+{
+ static HINTERNET session;
+
+ if (clear)
+ {
+ if (session)
+ {
+ WinHttpCloseHandle (session);
+ session = NULL;
+ }
+ return NULL;
+ }
+
+ if (!session)
+ {
+ session = WinHttpOpen (L"GnuPG dirmngr",
+ WINHTTP_ACCESS_TYPE_NO_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+ if (!session)
+ {
+ log_error ("WinHttpOpen failed: %s\n", w32_strerror (-1));
+ return NULL;
+ }
+ }
+
+ return session;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Return a proxy using a Windows API. */
+#ifdef HAVE_W32_SYSTEM
+static char *
+w32_get_proxy (const char *url)
+{
+ WINHTTP_AUTOPROXY_OPTIONS options = {0};
+ WINHTTP_PROXY_INFO info;
+ char *result = NULL;
+ char *p;
+ wchar_t *wurl;
+ int defaultcfg = 0;
+
+ wurl = utf8_to_wchar (url);
+ if (!wurl)
+ {
+ log_error ("utf8_to_wchar failed: %s\n",
+ gpg_strerror (gpg_error_from_syserror ()));
+ return NULL;
+ }
+
+ options.dwFlags = (WINHTTP_AUTOPROXY_ALLOW_AUTOCONFIG
+ | WINHTTP_AUTOPROXY_ALLOW_CM
+ | WINHTTP_AUTOPROXY_ALLOW_STATIC
+ | WINHTTP_AUTOPROXY_AUTO_DETECT
+ | WINHTTP_AUTOPROXY_SORT_RESULTS);
+ options.dwAutoDetectFlags = (WINHTTP_AUTO_DETECT_TYPE_DHCP
+ | WINHTTP_AUTO_DETECT_TYPE_DNS_A);
+ options.fAutoLogonIfChallenged = TRUE;
+
+ if (opt_debug)
+ log_debug ("calling WinHttpGetProxyForUrl (%s)\n", url);
+ if (!WinHttpGetProxyForUrl (w32_get_internet_session (0),
+ wurl, &options, &info))
+ {
+ int ec = (int)GetLastError ();
+ if (ec == ERROR_WINHTTP_AUTODETECTION_FAILED)
+ {
+ if (opt_debug)
+ log_debug ("calling WinHttpGetDefaultProxyConfiguration\n");
+ if (!WinHttpGetDefaultProxyConfiguration (&info))
+ {
+ if (opt_verbose)
+ log_info ("WinHttpGetDefaultProxyConfiguration failed: "
+ "%s (%d)\n", w32_strerror (ec), ec);
+ xfree (wurl);
+ return NULL;
+ }
+ defaultcfg = 1;
+ }
+ else
+ {
+ if (opt_verbose)
+ log_info ("WinHttpGetProxyForUrl failed: %s (%d)\n",
+ w32_strerror (ec), ec);
+ xfree (wurl);
+ return NULL;
+ }
+ }
+ xfree (wurl);
+
+ if (info.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY)
+ {
+ result = wchar_to_utf8 (info.lpszProxy);
+ if (!result)
+ log_error ("wchar_to_utf8 failed: %s\n",
+ gpg_strerror (gpg_error_from_syserror ()));
+ else
+ {
+ if (opt_debug)
+ log_debug ("proxies to use: '%s'\n", result);
+ /* The returned proxies are delimited by whitespace or
+ * semicolons. We return only the first proxy. */
+ for (p=result; *p; p++)
+ if (spacep (p) || *p == ';')
+ {
+ *p = 0;
+ break;
+ }
+ }
+ }
+ else if (info.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY)
+ {
+ /* No proxy shall be used. */
+ }
+ else
+ log_error ("%s returned unexpected code %lu\n",
+ defaultcfg? "WinHttpGetDefaultProxyConfiguration"
+ :"WinHttpGetProxyForUrl", info.dwAccessType);
+
+ if (info.lpszProxy)
+ GlobalFree (info.lpszProxy);
+ if (info.lpszProxyBypass)
+ GlobalFree (info.lpszProxyBypass);
+ return result;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Return the proxy to be used for the URL or host specified in HD.
+ * If OVERRIDE_PROXY is not NULL and not empty, this proxy will be
+ * used instead of any configured or dynamically determined proxy. If
+ * the function runs into an error an error code is returned and NULL
+ * is stored at R_PROXY. If the fucntion was successful and a proxy
+ * is to be used, information on the procy is stored at R_PROXY; if no
+ * proxy shall be used R_PROXY is set to NULL. Caller should always
+ * use release_proxy_info on the value stored at R_PROXY. */
+static gpg_error_t
+get_proxy_for_url (http_t hd, const char *override_proxy, proxy_info_t *r_proxy)
+{
+ gpg_error_t err = 0;
+ const char *proxystr, *s;
+ proxy_info_t proxy;
+#ifdef HAVE_W32_SYSTEM
+ char *proxystrbuf = NULL;
+#endif
+
+ *r_proxy = NULL;
+
+ if (override_proxy && *override_proxy)
+ proxystr = override_proxy;
+ else if (!(hd->flags & HTTP_FLAG_TRY_PROXY))
+ return 0; /* --honor-http-proxy not active */
+ else if ((s = getenv (HTTP_PROXY_ENV)) && *s)
+ proxystr = s;
+#ifdef HAVE_W32_SYSTEM
+ else if (hd->uri && hd->uri->original
+ && (proxystrbuf = w32_get_proxy (hd->uri->original)))
+ proxystr = proxystrbuf;
+#endif
+ else
+ return 0; /* No proxy known. */
+
+ proxy = xtrycalloc (1, sizeof *proxy);
+ if (!proxy)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating memory for proxy\n");
+ goto leave;
+ }
+
+ err = parse_uri (&proxy->uri, proxystr, 0, 0);
+ if (gpg_err_code (err) == GPG_ERR_INV_URI
+ && is_hostname_port (proxystr))
+ {
+ /* Retry assuming a "hostname:port" string. */
+ char *tmpname = strconcat ("http://", proxystr, NULL);
+ if (!tmpname)
+ err = gpg_error_from_syserror ();
+ else if (!parse_uri (&proxy->uri, tmpname, 0, 0))
+ err = 0;
+ xfree (tmpname);
+ }
+
+ if (!err)
+ {
+ /* Get rid of the escapes in the authstring. */
+ if (proxy->uri->auth)
+ remove_escapes (proxy->uri->auth);
+
+ if (!strcmp (proxy->uri->scheme, "http"))
+ proxy->is_http_proxy = 1;
+ else if (!strcmp (proxy->uri->scheme, "socks4")
+ || !strcmp (proxy->uri->scheme, "socks5h"))
+ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ else
+ err = gpg_error (GPG_ERR_INV_URI);
+
+ if (err)
+ {
+ log_error ("invalid HTTP proxy (%s): %s\n",
+ proxystr, gpg_strerror (err));
+ err = gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION);
+ }
+ else if (opt_verbose)
+ log_info ("using '%s' to proxy '%s'\n",
+ proxystr, hd->uri? hd->uri->original : NULL);
+ }
+
+ leave:
+#ifdef HAVE_W32_SYSTEM
+ xfree (proxystrbuf);
+#endif
+ if (err)
+ xfree (proxy);
+ else
+ *r_proxy = proxy;
+ return err;
+}
+
+
+/* Some checks done by send_request. */
+static gpg_error_t
+send_request_basic_checks (http_t hd)
+{
+ int mode;
if (hd->uri->use_tls && !hd->session)
{
log_error ("TLS requested but no session object provided\n");
- return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
+ return gpg_error (GPG_ERR_INTERNAL);
}
if (hd->uri->use_tls && !hd->session->tls_session)
{
log_error ("TLS requested but no TLS context available\n");
- return gpg_err_make (default_errsource, GPG_ERR_INTERNAL);
+ return gpg_error (GPG_ERR_INTERNAL);
}
if (opt_debug)
log_debug ("Using TLS library: %s %s\n",
@@ -1827,37 +2131,35 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
#endif /*HTTP_USE_GNUTLS*/
);
- if ((hd->flags & HTTP_FLAG_FORCE_TOR))
+ if ((hd->flags & HTTP_FLAG_FORCE_TOR)
+ && (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode))
{
- int mode;
-
- if (assuan_sock_get_flag (ASSUAN_INVALID_FD, "tor-mode", &mode) || !mode)
- {
- log_error ("Tor support is not available\n");
- return gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
- }
- /* Non-blocking connects do not work with our Tor proxy because
- * we can't continue the Socks protocol after the EINPROGRESS.
- * Disable the timeout to use a blocking connect. */
- timeout = 0;
+ log_error ("Tor support is not available\n");
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
- server = *hd->uri->host ? hd->uri->host : "localhost";
- port = hd->uri->port ? hd->uri->port : 80;
+ return 0;
+}
+
+
+/* Helper for send_request to set the servername. */
+static gpg_error_t
+send_request_set_sni (http_t hd, const char *name)
+{
+ gpg_error_t err = 0;
+# if HTTP_USE_GNUTLS
+ int rc;
+# endif
/* Try to use SNI. */
if (hd->uri->use_tls)
{
-#if HTTP_USE_GNUTLS
- int rc;
-#endif
-
xfree (hd->session->servername);
- hd->session->servername = xtrystrdup (httphost? httphost : server);
+ hd->session->servername = xtrystrdup (name);
if (!hd->session->servername)
{
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- return err;
+ err = gpg_error_from_syserror ();
+ goto leave;
}
#if HTTP_USE_NTBTLS
@@ -1866,7 +2168,7 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
if (err)
{
log_info ("ntbtls_set_hostname failed: %s\n", gpg_strerror (err));
- return err;
+ goto leave;
}
#elif HTTP_USE_GNUTLS
rc = gnutls_server_name_set (hd->session->tls_session,
@@ -1878,175 +2180,21 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
#endif /*HTTP_USE_GNUTLS*/
}
- if ( (proxy && *proxy)
- || ( (hd->flags & HTTP_FLAG_TRY_PROXY)
- && (http_proxy = getenv (HTTP_PROXY_ENV))
- && *http_proxy ))
- {
- parsed_uri_t uri;
+ leave:
+ return err;
+}
- if (proxy)
- http_proxy = proxy;
-
- err = parse_uri (&uri, http_proxy, 0, 0);
- if (gpg_err_code (err) == GPG_ERR_INV_URI
- && is_hostname_port (http_proxy))
- {
- /* Retry assuming a "hostname:port" string. */
- char *tmpname = strconcat ("http://", http_proxy, NULL);
- if (tmpname && !parse_uri (&uri, tmpname, 0, 0))
- err = 0;
- xfree (tmpname);
- }
-
- if (err)
- ;
- else if (!strcmp (uri->scheme, "http"))
- have_http_proxy = 1;
- else if (!strcmp (uri->scheme, "socks4")
- || !strcmp (uri->scheme, "socks5h"))
- err = gpg_err_make (default_errsource, GPG_ERR_NOT_IMPLEMENTED);
- else
- err = gpg_err_make (default_errsource, GPG_ERR_INV_URI);
-
- if (err)
- {
- log_error ("invalid HTTP proxy (%s): %s\n",
- http_proxy, gpg_strerror (err));
- return gpg_err_make (default_errsource, GPG_ERR_CONFIGURATION);
- }
-
- if (uri->auth)
- {
- remove_escapes (uri->auth);
- proxy_authstr = make_header_line ("Proxy-Authorization: Basic ",
- "\r\n",
- uri->auth, strlen(uri->auth));
- if (!proxy_authstr)
- {
- err = gpg_err_make (default_errsource,
- gpg_err_code_from_syserror ());
- http_release_parsed_uri (uri);
- return err;
- }
- }
-
- err = connect_server (ctrl,
- *uri->host ? uri->host : "localhost",
- uri->port ? uri->port : 80,
- hd->flags, NULL, timeout, &sock);
- http_release_parsed_uri (uri);
- }
- else
- {
- err = connect_server (ctrl,
- server, port, hd->flags, srvtag, timeout, &sock);
- }
-
- if (err)
- {
- xfree (proxy_authstr);
- return err;
- }
- hd->sock = my_socket_new (sock);
- if (!hd->sock)
- {
- xfree (proxy_authstr);
- return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- }
-
- if (have_http_proxy && hd->uri->use_tls)
- {
- int saved_flags;
- cookie_t cookie;
-
- /* Try to use the CONNECT method to proxy our TLS stream. */
- request = es_bsprintf
- ("CONNECT %s:%hu HTTP/1.0\r\nHost: %s:%hu\r\n%s",
- httphost ? httphost : server,
- port,
- httphost ? httphost : server,
- port,
- proxy_authstr ? proxy_authstr : "");
- xfree (proxy_authstr);
- proxy_authstr = NULL;
-
- if (! request)
- return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
-
- if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
- log_debug_string (request, "http.c:request:");
-
- cookie = xtrycalloc (1, sizeof *cookie);
- if (! cookie)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- xfree (request);
- return err;
- }
- cookie->sock = my_socket_ref (hd->sock);
- hd->write_cookie = cookie;
-
- hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
- if (! hd->fp_write)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- my_socket_unref (cookie->sock, NULL, NULL);
- xfree (cookie);
- xfree (request);
- hd->write_cookie = NULL;
- return err;
- }
- else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
-
- xfree (request);
- request = NULL;
-
- /* Make sure http_wait_response doesn't close the stream. */
- saved_flags = hd->flags;
- hd->flags &= ~HTTP_FLAG_SHUTDOWN;
-
- /* Get the response. */
- err = http_wait_response (hd);
-
- /* Restore flags, destroy stream. */
- hd->flags = saved_flags;
- es_fclose (hd->fp_read);
- hd->fp_read = NULL;
- hd->read_cookie = NULL;
-
- /* Reset state. */
- hd->in_data = 0;
-
- if (err)
- return err;
-
- if (hd->status_code != 200)
- {
- request = es_bsprintf
- ("CONNECT %s:%hu",
- httphost ? httphost : server,
- port);
-
- log_error (_("error accessing '%s': http status %u\n"),
- request ? request : "out of core",
- http_get_status_code (hd));
-
- xfree (request);
- return gpg_error (GPG_ERR_NO_DATA);
- }
-
- /* We are done with the proxy, the code below will establish a
- * TLS session and talk directly to the target server. */
- http_proxy = NULL;
- }
+/* Run the NTBTLS handshake if needed. */
#if HTTP_USE_NTBTLS
+static gpg_error_t
+run_ntbtls_handshake (http_t hd)
+{
+ gpg_error_t err;
+ estream_t in, out;
+
if (hd->uri->use_tls)
{
- estream_t in, out;
-
my_socket_ref (hd->sock);
/* Until we support send/recv in estream under Windows we need
@@ -2060,8 +2208,7 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
if (!in)
{
err = gpg_error_from_syserror ();
- xfree (proxy_authstr);
- return err;
+ goto leave;
}
# ifdef HAVE_W32_SYSTEM
@@ -2074,8 +2221,7 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{
err = gpg_error_from_syserror ();
es_fclose (in);
- xfree (proxy_authstr);
- return err;
+ goto leave;
}
err = ntbtls_set_transport (hd->session->tls_session, in, out);
@@ -2083,8 +2229,9 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{
log_info ("TLS set_transport failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
- xfree (proxy_authstr);
- return err;
+ es_fclose (in);
+ es_fclose (out);
+ goto leave;
}
if (hd->session->verify_cb)
@@ -2095,71 +2242,62 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{
log_error ("ntbtls_set_verify_cb failed: %s\n",
gpg_strerror (err));
- xfree (proxy_authstr);
- return err;
+ goto leave;
}
}
while ((err = ntbtls_handshake (hd->session->tls_session)))
{
-#if NTBTLS_VERSION_NUMBER >= 0x000200
unsigned int tlevel, ttype;
- const char *s = ntbtls_get_last_alert (hd->session->tls_session,
- &tlevel, &ttype);
+ const char *s;
+
+ s = ntbtls_get_last_alert (hd->session->tls_session, &tlevel, &ttype);
if (s)
log_info ("TLS alert: %s (%u.%u)\n", s, tlevel, ttype);
-#endif
switch (err)
{
default:
log_info ("TLS handshake failed: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
- xfree (proxy_authstr);
- return err;
+ goto leave;
}
}
hd->session->verify.done = 0;
- /* Try the available verify callbacks until one returns success
- * or a real error. Note that NTBTLS does the verification
- * during the handshake via */
- err = 0; /* Fixme check that the CB has been called. */
+ /* Note that in contrast to GNUTLS NTBTLS uses a registered
+ * callback to run the verification as part of the handshake. */
+ err = 0;
+ /* FIXME: We could check that the CB has been called and if not
+ * error out with this warning:
+ * if (err)
+ * {
+ * log_info ("TLS connection authentication failed: %s <%s>\n",
+ * gpg_strerror (err), gpg_strsource (err));
+ * goto leave;
+ * }
+ */
+ }
+ else
+ err = 0;
- if (hd->session->verify_cb
- && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR
- && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
- err = hd->session->verify_cb (hd->session->verify_cb_value,
- hd, hd->session,
- (hd->flags | hd->session->flags),
- hd->session->tls_session);
+ leave:
+ return err;
+}
+#endif /*HTTP_USE_NTBTLS*/
- if (tls_callback
- && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR
- && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
- err = tls_callback (hd, hd->session, 0);
- if (gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR
- && gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED)
- err = http_verify_server_credentials (hd->session);
-
- if (err)
- {
- log_info ("TLS connection authentication failed: %s <%s>\n",
- gpg_strerror (err), gpg_strsource (err));
- xfree (proxy_authstr);
- return err;
- }
-
- }
-
-#elif HTTP_USE_GNUTLS
+/* Run the GNUTLS handshake if needed. */
+#if HTTP_USE_GNUTLS
+static gpg_error_t
+run_gnutls_handshake (http_t hd, const char *server)
+{
+ gpg_error_t err;
+ int rc;
if (hd->uri->use_tls)
{
- int rc;
-
my_socket_ref (hd->sock);
gnutls_transport_set_ptr (hd->session->tls_session, hd->sock);
gnutls_transport_set_pull_function (hd->session->tls_session,
@@ -2195,8 +2333,8 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
}
else
log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc));
- xfree (proxy_authstr);
- return gpg_err_make (default_errsource, GPG_ERR_NETWORK);
+ err = gpg_error (GPG_ERR_NETWORK);
+ goto leave;
}
hd->session->verify.done = 0;
@@ -2208,12 +2346,586 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
{
log_info ("TLS connection authentication failed: %s\n",
gpg_strerror (err));
- xfree (proxy_authstr);
- return err;
+ goto leave;
+ }
+ }
+ else
+ err =0;
+
+ leave:
+ return err;
+}
+#endif /*HTTP_USE_GNUTLS*/
+
+
+/* It INPUTSTRING is NULL get the intial token. If INPUTSTRING is not
+ * NULL, decode the string and use this as input from teh server. On
+ * success the final output token is stored at PROXY->OUTTOKEN and
+ * OUTTOKLEN. IF the authentication succeeded OUTTOKLEN is zero. */
+#ifdef USE_TLS
+static gpg_error_t
+proxy_get_token (proxy_info_t proxy, const char *inputstring)
+{
+#ifdef HAVE_W32_SYSTEM
+ gpg_error_t err;
+ int rc;
+ SecBuffer chlg_buf; /* challenge buffer */
+ SecBufferDesc chlg_desc; /* challenge descriptor */
+ SecBuffer resp_buf; /* response buffer */
+ SecBufferDesc resp_desc; /* response descriptor */
+ unsigned long attrs;
+ TimeStamp expiry; /* (value not used) */
+ void *intoken = NULL;
+ size_t intoklen;
+
+ if (inputstring)
+ {
+ /* The input is expected in the token parameter but the paremter
+ * name is often forgotten. Thus we simply detect the parameter
+ * name and skip it, assuming no other parameters are given. */
+ if (!strncmp (inputstring, "token=", 6))
+ inputstring += 6;
+
+ err = b64decode (inputstring, NULL, &intoken, &intoklen);
+ /* Just to be safe that we don't overflow an ulong we check the
+ * actual size against an arbitrary limit. */
+ if (!err && intoklen > 65535)
+ err = gpg_error (GPG_ERR_ERANGE);
+ if (err || !intoklen)
+ {
+ log_error ("error decoding received auth token: %s\n",
+ err? gpg_strerror (err):"empty challenge token received");
+ if (!err)
+ err = gpg_error (GPG_ERR_BAD_AUTH);
+ goto leave;
}
}
-#endif /*HTTP_USE_GNUTLS*/
+ if (!proxy->spn)
+ {
+ char *buffer = strconcat ("HTTP/", (*proxy->uri->host
+ ?proxy->uri->host:"localhost"), NULL);
+ if (!buffer)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: using '%s' as SPN\n", buffer);
+ proxy->spn = utf8_to_wchar (buffer);
+ xfree (buffer);
+ if (!proxy->spn)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ if (!proxy->token_size || !proxy->outtoken) /* Not yet initialized. */
+ {
+ PSecPkgInfoW pinfo;
+
+ rc = QuerySecurityPackageInfoW (NEGOSSP_NAME_W, &pinfo);
+ if (rc)
+ {
+ log_error ("QSPI(Negotiate) failed: %s (%d)\n",
+ w32_strerror (rc), rc);
+ err = gpg_error (GPG_ERR_BAD_AUTH);
+ goto leave;
+ }
+ proxy->token_size = pinfo->cbMaxToken;
+ FreeContextBuffer (pinfo);
+
+ proxy->outtoken = xtrymalloc (proxy->token_size);
+ if (!proxy->outtoken)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ if (!proxy->cred_handle_valid)
+ {
+ rc = AcquireCredentialsHandleW (NULL, NEGOSSP_NAME_W,
+ SECPKG_CRED_OUTBOUND, NULL,
+ NULL, /* Current user */
+ NULL, /* reserved */
+ NULL, /* reserved */
+ &proxy->cred_handle,
+ NULL /* expiry */);
+ if (rc)
+ {
+ log_error ("ACH(Negotiate) failed: %s (%d)\n", w32_strerror (rc), rc);
+ err = gpg_error (GPG_ERR_NO_AUTH);
+ goto leave;
+ }
+ proxy->cred_handle_valid = 1;
+ }
+
+ /* Now generate our challenge-response message. */
+ if (intoken)
+ {
+ chlg_buf.BufferType = SECBUFFER_TOKEN;
+ chlg_buf.pvBuffer = intoken;
+ chlg_buf.cbBuffer = intoklen;
+ chlg_desc.ulVersion = SECBUFFER_VERSION;
+ chlg_desc.cBuffers = 1;
+ chlg_desc.pBuffers = &chlg_buf;
+ }
+
+ resp_buf.BufferType = SECBUFFER_TOKEN;
+ resp_buf.pvBuffer = proxy->outtoken;
+ resp_buf.cbBuffer = proxy->token_size;
+ resp_desc.ulVersion = SECBUFFER_VERSION;
+ resp_desc.cBuffers = 1;
+ resp_desc.pBuffers = &resp_buf;
+ rc = InitializeSecurityContextW (&proxy->cred_handle,
+ (intoken && proxy->ctxt_handle_valid)
+ ? &proxy->ctxt_handle : NULL,
+ proxy->spn, /* service principal name */
+ ISC_REQ_CONFIDENTIALITY,
+ 0, /* reserved */
+ SECURITY_NATIVE_DREP,
+ intoken? &chlg_desc : NULL,
+ 0, /* reserved */
+ &proxy->ctxt_handle, /* new context */
+ &resp_desc, /* the output. */
+ &attrs, /* attribs of the context. */
+ &expiry);
+ switch (rc)
+ {
+ case SEC_E_OK: /* All done and no more ISC expected. */
+ break;
+
+ case SEC_I_COMPLETE_AND_CONTINUE: /* Need to call CompleteAuthToken. */
+ case SEC_I_COMPLETE_NEEDED:
+ rc = CompleteAuthToken (&proxy->ctxt_handle, &resp_desc);
+ log_error ("CompleteAuthToken failed: %s (%d)\n", w32_strerror (rc), rc);
+ err = gpg_error (GPG_ERR_NO_AUTH);
+ goto leave;
+ break;
+
+ case SEC_I_CONTINUE_NEEDED: /* Send the new token to the client. */
+ break;
+
+ default:
+ log_error ("ISC(Negotiate) failed: %s (%d)\n", w32_strerror (rc), rc);
+ err = gpg_error (GPG_ERR_NO_AUTH);
+ goto leave;
+ }
+
+ proxy->outtoklen = resp_buf.cbBuffer;
+ proxy->ctxt_handle_valid = 1;
+ err = 0;
+
+ leave:
+ xfree (intoken);
+ return err;
+
+#else /*!HAVE_W32_SYSTEM*/
+
+ (void)proxy;
+ (void)inputstring;
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+#endif /*!HAVE_W32_SYSTEM*/
+}
+#endif /*USE_TLS*/
+
+
+/* Use the CONNECT method to proxy our TLS stream. */
+#ifdef USE_TLS
+static gpg_error_t
+run_proxy_connect (http_t hd, proxy_info_t proxy,
+ const char *httphost, const char *server,
+ unsigned short port)
+{
+ gpg_error_t err;
+ int saved_flags = hd->flags;
+ char *authhdr = NULL;
+ char *request = NULL;
+ char *tmpstr = NULL;
+ const char *s, *parms;
+ unsigned int idx;
+ int auth_basic = 0;
+ enum auth_negotiate_states authstate = 0;
+ unsigned int authpasses = 0;
+
+ /* Authentication methods implemented here:
+ * RFC-2617 - HTTP Authentication: Basic and Digest Access Authentication
+ * RFC-4559 - SPNEGO-based Kerberos and NTLM HTTP Authentication
+ */
+ auth_basic = !!proxy->uri->auth;
+
+ /* For basic authentication we need to send just one request. */
+ if (auth_basic
+ && !(authhdr = make_header_line ("Proxy-Authorization: Basic ",
+ "\r\n",
+ proxy->uri->auth,
+ strlen (proxy->uri->auth))))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ again:
+ xfree (request);
+ request = es_bsprintf ("CONNECT %s:%hu HTTP/1.%c\r\nHost: %s:%hu\r\n%s%s",
+ httphost ? httphost : server,
+ port,
+ auth_basic? '0' : '1',
+ httphost ? httphost : server,
+ port,
+ authhdr ? authhdr : "",
+ auth_basic? "" : "Connection: keep-alive\r\n");
+ if (!request)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ hd->keep_alive = !auth_basic; /* We may need to send more requests. */
+
+ if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
+ log_debug_with_string (request, "http.c:proxy:request:");
+
+ if (!hd->fp_write)
+ {
+ err = make_fp_write (hd, 0, NULL);
+ if (err)
+ goto leave;
+ }
+
+ if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Make sure http_wait_response doesn't close the stream. */
+ saved_flags = hd->flags;
+ hd->flags &= ~HTTP_FLAG_SHUTDOWN;
+
+ /* Get the response and set hd->fp_read */
+ err = http_wait_response (hd);
+ if (err)
+ goto leave;
+
+ {
+ unsigned long count = 0;
+
+ while (es_getc (hd->fp_read) != EOF)
+ count++;
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: skipped %lu bytes of response-body\n",
+ count);
+ }
+
+ /* Reset state. */
+ es_clearerr (hd->fp_read);
+ ((cookie_t)(hd->read_cookie))->up_to_empty_line = 1;
+ hd->in_data = 0;
+
+ if (hd->status_code >= 200 && hd->status_code < 300 )
+ err = 0; /* Success. */
+ else if (hd->status_code == 407)
+ {
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: 407 seen\n");
+ parms = NULL;
+ for (idx=0; (s = http_get_header (hd, "Proxy-Authenticate", idx)); idx++)
+ {
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: method=%s\n", s);
+ if (!parms)
+ parms = has_leading_keyword (s, "Negotiate");
+ }
+ if (!parms)
+ authstate = AUTH_NGT_NONE;
+ else if (authstate == AUTH_NGT_NONE)
+ authstate = AUTH_NGT_RCVD;
+
+ switch (authstate)
+ {
+ case AUTH_NGT_NONE:
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: no supported auth method\n");
+ err = gpg_error (GPG_ERR_NO_AUTH);
+ break;
+
+ case AUTH_NGT_RCVD:
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: using negotiate - init\n");
+ err = proxy_get_token (proxy, NULL);
+ if (err)
+ goto leave;
+ if (proxy->outtoklen) /* Authentication needs to continue. */
+ {
+ xfree (authhdr);
+ authhdr = make_header_line ("Proxy-Authorization: Negotiate ",
+ "\r\n",
+ proxy->outtoken, proxy->outtoklen);
+ if (!authhdr)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ authstate = AUTH_NGT_SENT;
+ authpasses++;
+ goto again;
+ }
+ break;
+
+ case AUTH_NGT_SENT:
+ if (opt_debug)
+ log_debug ("http.c:proxy_connect: using negotiate - next\n");
+ if (!*parms)
+ {
+ log_debug ("proxy authentication failed"
+ " due to server not accepting our challenge\n");
+ err = gpg_error (GPG_ERR_BAD_AUTH);
+ goto leave;
+ }
+ if (authpasses > 5)
+ {
+ log_error ("proxy authentication failed"
+ " due to too many passes\n");
+ err = gpg_error (GPG_ERR_BAD_AUTH);
+ goto leave;
+
+ }
+ err = proxy_get_token (proxy, parms);
+ if (err)
+ goto leave;
+ if (proxy->outtoklen) /* Authentication needs to continue. */
+ {
+ xfree (authhdr);
+ authhdr = make_header_line ("Proxy-Authorization: Negotiate ",
+ "\r\n",
+ proxy->outtoken, proxy->outtoklen);
+ if (!authhdr)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ authpasses++;
+ goto again;
+ }
+ break;
+
+ default:
+ BUG();
+ }
+ }
+ else
+ err = gpg_error (GPG_ERR_NO_DATA);
+
+ if (err)
+ {
+ xfree (tmpstr);
+ tmpstr = es_bsprintf ("%s:%hu", httphost ? httphost : server, port);
+ log_error (_("error accessing '%s': http status %u\n"),
+ tmpstr ? tmpstr : "out of core",
+ http_get_status_code (hd));
+ goto leave;
+ }
+
+ leave:
+ /* Restore flags, destroy stream, reset state. */
+ hd->flags = saved_flags;
+ es_fclose (hd->fp_read);
+ hd->fp_read = NULL;
+ hd->read_cookie = NULL;
+ hd->keep_alive = 0;
+ hd->in_data = 0;
+
+ xfree (request);
+ xfree (authhdr);
+ xfree (tmpstr);
+ return err;
+}
+#endif /*USE_TLS*/
+
+
+/* Make a request string using a standard proxy. On success the
+ * request is stored at R_REQUEST (and will never be NULL). */
+static gpg_error_t
+mk_proxy_request (http_t hd, proxy_info_t proxy,
+ const char *httphost, const char *server,
+ unsigned short port, const char *relpath,
+ const char *authstr,
+ char **r_request)
+{
+ gpg_error_t err = 0;
+ char *authhdr = NULL;
+ char *request = NULL;
+
+ *r_request = NULL;
+
+ if (proxy->uri->auth
+ && !(authhdr = make_header_line ("Proxy-Authorization: Basic ",
+ "\r\n",
+ proxy->uri->auth,
+ strlen (proxy->uri->auth))))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ request = es_bsprintf ("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s",
+ hd->req_type == HTTP_REQ_GET ? "GET" :
+ hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
+ hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
+ hd->uri->use_tls? "https" : "http",
+ httphost? httphost : server,
+ port, *relpath == '/' ? "" : "/", relpath,
+ authstr ? authstr : "",
+ authhdr ? authhdr : "");
+ if (!request)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ *r_request = request;
+ request = NULL;
+
+ leave:
+ xfree (request);
+ xfree (authhdr);
+ return err;
+}
+
+
+/* Make a request string using. On success the request is stored at
+ * R_REQUEST (and will never be NULL). */
+static gpg_error_t
+mk_std_request (http_t hd,
+ const char *httphost, const char *server,
+ unsigned short port, const char *relpath,
+ const char *authstr,
+ char **r_request)
+{
+ gpg_error_t err = 0;
+ char portstr[35];
+ char *request = NULL;
+
+ *r_request = NULL;
+
+ if (port == (hd->uri->use_tls? 443 : 80))
+ *portstr = 0;
+ else
+ snprintf (portstr, sizeof portstr, ":%u", port);
+
+ request = es_bsprintf ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
+ hd->req_type == HTTP_REQ_GET ? "GET" :
+ hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
+ hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
+ *relpath == '/' ? "" : "/", relpath,
+ httphost? httphost : server,
+ portstr,
+ authstr? authstr:"");
+ if (!request)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ *r_request = request;
+ request = NULL;
+
+ leave:
+ xfree (request);
+ return err;
+}
+
+
+/*
+ * Send a HTTP request to the server
+ * Returns 0 if the request was successful
+ */
+static gpg_error_t
+send_request (ctrl_t ctrl,
+ http_t hd, const char *httphost, const char *auth,
+ const char *override_proxy,
+ const char *srvtag, unsigned int timeout,
+ strlist_t headers)
+{
+ gpg_error_t err;
+ const char *server;
+ char *request = NULL;
+ char *relpath = NULL;
+ unsigned short port;
+ int use_http_proxy = 0;
+ char *proxy_authstr = NULL;
+ char *authstr = NULL;
+ assuan_fd_t sock;
+ proxy_info_t proxy = NULL;
+
+ err = send_request_basic_checks (hd);
+ if (err)
+ goto leave;
+
+ if ((hd->flags & HTTP_FLAG_FORCE_TOR))
+ {
+ /* Non-blocking connects do not work with our Tor proxy because
+ * we can't continue the Socks protocol after the EINPROGRESS.
+ * Disable the timeout to use a blocking connect. */
+ timeout = 0;
+ }
+
+ server = *hd->uri->host ? hd->uri->host : "localhost";
+ port = hd->uri->port ? hd->uri->port : 80;
+
+ if ((err = send_request_set_sni (hd, httphost? httphost : server)))
+ goto leave;
+
+ if ((err = get_proxy_for_url (hd, override_proxy, &proxy)))
+ goto leave;
+
+ if (proxy && proxy->is_http_proxy)
+ {
+ use_http_proxy = 1; /* We want to use a proxy for the conenction. */
+ err = connect_server (ctrl,
+ *proxy->uri->host ? proxy->uri->host : "localhost",
+ proxy->uri->port ? proxy->uri->port : 80,
+ hd->flags, NULL, timeout, &sock);
+ }
+ else
+ {
+ err = connect_server (ctrl,
+ server, port, hd->flags, srvtag, timeout, &sock);
+ }
+ if (err)
+ goto leave;
+
+ hd->sock = my_socket_new (sock);
+ if (!hd->sock)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+#if USE_TLS
+ if (use_http_proxy && hd->uri->use_tls)
+ {
+ err = run_proxy_connect (hd, proxy, httphost, server, port);
+ if (err)
+ goto leave;
+
+ /* We are done with the proxy, the code below will establish a
+ * TLS session and talk directly to the target server. Thus we
+ * clear the flag to indicate this. */
+ use_http_proxy = 0;
+ }
+#endif /* USE_TLS */
+
+#if HTTP_USE_NTBTLS
+ err = run_ntbtls_handshake (hd);
+#elif HTTP_USE_GNUTLS
+ err = run_gnutls_handshake (hd, server);
+#else
+ err = 0;
+#endif
+ if (err)
+ goto leave;
if (auth || hd->uri->auth)
{
@@ -2224,9 +2936,8 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
myauth = xtrystrdup (auth);
if (!myauth)
{
- xfree (proxy_authstr);
- return gpg_err_make (default_errsource,
- gpg_err_code_from_syserror ());
+ err = gpg_error_from_syserror ();
+ goto leave;
}
remove_escapes (myauth);
}
@@ -2238,119 +2949,65 @@ send_request (ctrl_t ctrl, http_t hd, const char *httphost, const char *auth,
authstr = make_header_line ("Authorization: Basic ", "\r\n",
myauth, strlen (myauth));
- if (auth)
+ if (auth) /* (Was allocated.) */
xfree (myauth);
if (!authstr)
{
- xfree (proxy_authstr);
- return gpg_err_make (default_errsource,
- gpg_err_code_from_syserror ());
+ err = gpg_error_from_syserror ();
+ goto leave;
}
}
- p = build_rel_path (hd->uri);
- if (!p)
+ relpath = build_rel_path (hd->uri);
+ if (!relpath)
{
- xfree (authstr);
- xfree (proxy_authstr);
- return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
+ err = gpg_error_from_syserror ();
+ goto leave;
}
- if (http_proxy && *http_proxy)
- {
- request = es_bsprintf
- ("%s %s://%s:%hu%s%s HTTP/1.0\r\n%s%s",
- hd->req_type == HTTP_REQ_GET ? "GET" :
- hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
- hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
- hd->uri->use_tls? "https" : "http",
- httphost? httphost : server,
- port, *p == '/' ? "" : "/", p,
- authstr ? authstr : "",
- proxy_authstr ? proxy_authstr : "");
- }
+ if (use_http_proxy)
+ err = mk_proxy_request (hd, proxy, httphost, server, port,
+ relpath, authstr, &request);
else
- {
- char portstr[35];
-
- if (port == (hd->uri->use_tls? 443 : 80))
- *portstr = 0;
- else
- snprintf (portstr, sizeof portstr, ":%u", port);
-
- request = es_bsprintf
- ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
- hd->req_type == HTTP_REQ_GET ? "GET" :
- hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
- hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
- *p == '/' ? "" : "/", p,
- httphost? httphost : server,
- portstr,
- authstr? authstr:"");
- }
- xfree (p);
- if (!request)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- xfree (authstr);
- xfree (proxy_authstr);
- return err;
- }
+ err = mk_std_request (hd, httphost, server, port,
+ relpath, authstr, &request);
+ if (err)
+ goto leave;
if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
log_debug_string (request, "http.c:request:");
/* First setup estream so that we can write even the first line
using estream. This is also required for the sake of gnutls. */
- {
- cookie_t cookie;
+ err = make_fp_write (hd, hd->uri->use_tls, hd->session);
+ if (err)
+ goto leave;
- cookie = xtrycalloc (1, sizeof *cookie);
- if (!cookie)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- goto leave;
- }
- cookie->sock = my_socket_ref (hd->sock);
- hd->write_cookie = cookie;
- cookie->use_tls = hd->uri->use_tls;
- cookie->session = http_session_ref (hd->session);
-
- hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
- if (!hd->fp_write)
- {
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- my_socket_unref (cookie->sock, NULL, NULL);
- xfree (cookie);
- hd->write_cookie = NULL;
- }
- else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
- err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
- else
- err = 0;
-
- if (!err)
+ if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
{
- for (;headers; headers=headers->next)
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ for (;headers; headers=headers->next)
+ {
+ if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
+ log_debug_string (headers->d, "http.c:request-header:");
+ if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write))
+ || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write)))
{
- if (opt_debug || (hd->flags & HTTP_FLAG_LOG_RESP))
- log_debug_string (headers->d, "http.c:request-header:");
- if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write))
- || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write)))
- {
- err = gpg_err_make (default_errsource,
- gpg_err_code_from_syserror ());
- break;
- }
+ err = gpg_error_from_syserror ();
+ goto leave;
}
}
- }
leave:
es_free (request);
xfree (authstr);
xfree (proxy_authstr);
+ xfree (relpath);
+ release_proxy_info (proxy);
return err;
}
@@ -2476,19 +3133,26 @@ store_header (http_t hd, char *line)
p++;
value = p;
- for (h=hd->headers; h; h = h->next)
- if ( !strcmp (h->name, line) )
- break;
- if (h)
+ /* Check whether we have already seen a line with that name. In
+ * that case we assume it is a comma separated list and merge
+ * them. Of course there are a few exceptions. */
+ if (!strcmp (line, "Proxy-Authenticate")
+ || !strcmp (line, "Www-Authenticate"))
+ ; /* Better to have them separate. */
+ else
{
- /* We have already seen a line with that name. Thus we assume
- * it is a comma separated list and merge them. */
- p = strconcat (h->value, ",", value, NULL);
- if (!p)
- return gpg_err_code_from_syserror ();
- xfree (h->value);
- h->value = p;
- return 0;
+ for (h=hd->headers; h; h = h->next)
+ if ( !strcmp (h->name, line) )
+ break;
+ if (h)
+ {
+ p = strconcat (h->value, ",", value, NULL);
+ if (!p)
+ return gpg_err_code_from_syserror ();
+ xfree (h->value);
+ h->value = p;
+ return 0;
+ }
}
/* Append a new header. */
@@ -2511,18 +3175,27 @@ store_header (http_t hd, char *line)
/* Return the header NAME from the last response. The returned value
- is valid as along as HD has not been closed and no other request
- has been send. If the header was not found, NULL is returned. NAME
- must be canonicalized, that is the first letter of each dash
- delimited part must be uppercase and all other letters lowercase. */
+ * is valid as along as HD has not been closed and no other request
+ * has been send. If the header was not found, NULL is returned. NAME
+ * must be canonicalized, that is the first letter of each dash
+ * delimited part must be uppercase and all other letters lowercase.
+ * SKIP gives the number of entries of the requested NAME to skip
+ * before returning; this can be used to enumerate headers with the
+ * same name (see store_header).
+*/
const char *
-http_get_header (http_t hd, const char *name)
+http_get_header (http_t hd, const char *name, unsigned int skip)
{
header_t h;
for (h=hd->headers; h; h = h->next)
- if ( !strcmp (h->name, name) )
- return h->value;
+ if (!strcmp (h->name, name))
+ {
+ if (skip)
+ skip--;
+ else
+ return h->value;
+ }
return NULL;
}
@@ -2645,7 +3318,7 @@ parse_response (http_t hd)
cookie->content_length_valid = 0;
if (!(hd->flags & HTTP_FLAG_IGNORE_CL))
{
- s = http_get_header (hd, "Content-Length");
+ s = http_get_header (hd, "Content-Length", 0);
if (s)
{
cookie->content_length_valid = 1;
@@ -3101,8 +3774,14 @@ connect_server (ctrl_t ctrl, const char *server, unsigned short port,
if (!connected)
{
if (!hostfound)
- log_error ("can't connect to '%s': %s\n",
- server, "host not found");
+ {
+ log_error ("can't connect to '%s': %s\n",
+ server, "host not found");
+ /* If the resolver told us "no name" translate this in this
+ * case to "unknown host". */
+ if (gpg_err_code (last_err) == GPG_ERR_NO_NAME)
+ last_err = 0;
+ }
else if (!anyhostaddr)
log_error ("can't connect to '%s': %s\n",
server, "no IP address for host");
@@ -3223,31 +3902,48 @@ cookie_read (void *cookie, void *buffer, size_t size)
{
cookie_t c = cookie;
int nread;
+ size_t offset = 0;
if (c->content_length_valid)
{
if (!c->content_length)
- return 0; /* EOF */
+ {
+ c->content_length_valid = 0;
+ return 0; /* EOF */
+ }
if (c->content_length < size)
size = c->content_length;
}
+ if (c->pending.len)
+ {
+ offset = c->pending.len > size? size : c->pending.len;
+ memcpy (buffer, c->pending.data, offset);
+ c->pending.len -= offset;
+ }
+
+ if (offset >= size)
+ nread = offset;
+ else
#if HTTP_USE_NTBTLS
if (c->use_tls && c->session && c->session->tls_session)
{
estream_t in, out;
ntbtls_get_stream (c->session->tls_session, &in, &out);
- nread = es_fread (buffer, 1, size, in);
+ nread = es_fread ((char*)buffer+offset, 1, size-offset, in);
if (opt_debug)
- log_debug ("TLS network read: %d/%zu\n", nread, size);
+ log_debug ("TLS network read: %d/%zu\n", nread, size-offset);
+ if (nread >= 0)
+ nread += offset;
}
else
#elif HTTP_USE_GNUTLS
if (c->use_tls && c->session && c->session->tls_session)
{
again:
- nread = gnutls_record_recv (c->session->tls_session, buffer, size);
+ nread = gnutls_record_recv (c->session->tls_session,
+ (char*)buffer+offset, size-offset);
if (nread < 0)
{
if (nread == GNUTLS_E_INTERRUPTED)
@@ -3274,11 +3970,86 @@ cookie_read (void *cookie, void *buffer, size_t size)
gpg_err_set_errno (EIO);
return -1;
}
+ if (nread >= 0)
+ nread += offset;
}
else
#endif /*HTTP_USE_GNUTLS*/
{
- nread = read_server (c->sock->fd, buffer, size);
+ nread = read_server (c->sock->fd, (char*)buffer+offset, size-offset);
+ if (opt_debug)
+ log_debug ("network read: %d/%zu\n", nread, size);
+ if (nread >= 0)
+ nread += offset;
+ }
+
+ if (nread > 0 && c->up_to_empty_line)
+ {
+ gpg_error_t err;
+ const char *s;
+ size_t n;
+ int extra;
+ int lfcr_pending = 0;
+ char *bufp = buffer;
+
+ if (c->last_was_lf && nread > 1 && bufp[0] == '\r' && bufp[1] == '\n')
+ {
+ s = buffer;
+ extra = 2;
+ }
+ else if (c->last_was_lf && bufp[0] == '\r')
+ {
+ lfcr_pending = 1;
+ s = buffer; /* Only to avoid the call to gnupg_memstr. */
+ }
+ else if (c->last_was_lfcr && bufp[0] == '\n')
+ {
+ s = buffer;
+ extra = 1;
+ }
+ else
+ s = NULL;
+
+ c->last_was_lfcr = c->last_was_lf = 0;
+
+ if (!s)
+ {
+ s = gnupg_memstr (buffer, nread, "\n\r\n");
+ extra = 3;
+ }
+
+ if (lfcr_pending)
+ c->last_was_lfcr = 1;
+ else if (s)
+ {
+ /* Save away the rest and return up to the LF. */
+ log_assert (!c->pending.len);
+ n = (s+extra) - bufp;
+ log_assert (n <= nread);
+ c->pending.len = nread - n;
+ if (!c->pending.data || c->pending.len >= c->pending.size)
+ {
+ xfree (c->pending.data);
+ c->pending.size = c->pending.len + 256; /* Some extra space. */
+ c->pending.data = xtrymalloc (c->pending.size);
+ if (!c->pending.data)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating network read buffer: %s\n",
+ gpg_strerror (err));
+ return -1;
+ }
+ memcpy (c->pending.data, bufp + n, c->pending.len);
+ }
+ else
+ memcpy (c->pending.data, bufp + n, c->pending.len);
+ nread = n; /* Return everything up to the empty line. */
+ c->up_to_empty_line = 0;
+ }
+ else if (bufp[nread-1] == '\n')
+ c->last_was_lf = 1;
+ else if (nread > 1 && bufp[nread-2] == '\n' && bufp[nread-1] == '\r')
+ c->last_was_lfcr = 1;
}
if (c->content_length_valid && nread > 0)
@@ -3439,6 +4210,7 @@ cookie_close (void *cookie)
if (c->session)
http_session_unref (c->session);
+ xfree (c->pending.data);
xfree (c);
return 0;
}
@@ -3447,7 +4219,7 @@ cookie_close (void *cookie)
/* Verify the credentials of the server. Returns 0 on success and
- store the result in the session object. */
+ store the result in the session object. Only used by GNUTLS. */
gpg_error_t
http_verify_server_credentials (http_session_t sess)
{
@@ -3481,19 +4253,15 @@ http_verify_server_credentials (http_session_t sess)
}
else if (status)
{
- log_error ("%s: status=0x%04x\n", errprefix, status);
-#if GNUTLS_VERSION_NUMBER >= 0x030104
- {
- gnutls_datum_t statusdat;
+ gnutls_datum_t statusdat;
- if (!gnutls_certificate_verification_status_print
- (status, GNUTLS_CRT_X509, &statusdat, 0))
- {
- log_info ("%s: %s\n", errprefix, statusdat.data);
- gnutls_free (statusdat.data);
- }
- }
-#endif /*gnutls >= 3.1.4*/
+ log_error ("%s: status=0x%04x\n", errprefix, status);
+ if (!gnutls_certificate_verification_status_print
+ (status, GNUTLS_CRT_X509, &statusdat, 0))
+ {
+ log_info ("%s: %s\n", errprefix, statusdat.data);
+ gnutls_free (statusdat.data);
+ }
sess->verify.status = status;
if (!err)
@@ -3834,3 +4602,13 @@ http_status2string (unsigned int status)
return "";
}
+
+
+/* Fucntion called on SIGHUP to flush internal variables. */
+void
+http_reinitialize (void)
+{
+#ifdef HAVE_W32_SYSTEM
+ w32_get_internet_session (1); /* Clear our session. */
+#endif /*HAVE_W32_SYSTEM*/
+}
diff --git a/dirmngr/http.h b/dirmngr/http.h
index e60212761..28406694e 100644
--- a/dirmngr/http.h
+++ b/dirmngr/http.h
@@ -132,9 +132,11 @@ typedef gpg_error_t (*http_verify_cb_t) (void *opaque,
void http_set_verbose (int verbose, int debug);
+/* The next three functions are only used with GNUTLS. */
void http_register_tls_callback (gpg_error_t (*cb)(http_t,http_session_t,int));
void http_register_tls_ca (const char *fname);
void http_register_cfg_ca (const char *fname);
+
void http_register_netactivity_cb (void (*cb)(void));
@@ -193,7 +195,7 @@ estream_t http_get_read_ptr (http_t hd);
estream_t http_get_write_ptr (http_t hd);
unsigned int http_get_status_code (http_t hd);
const char *http_get_tls_info (http_t hd, const char *what);
-const char *http_get_header (http_t hd, const char *name);
+const char *http_get_header (http_t hd, const char *name, unsigned int skip);
const char **http_get_header_names (http_t hd);
gpg_error_t http_verify_server_credentials (http_session_t sess);
diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c
index 66291bc02..75fe19987 100644
--- a/dirmngr/ks-engine-hkp.c
+++ b/dirmngr/ks-engine-hkp.c
@@ -1327,7 +1327,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
{
xfree (request_buffer);
err = http_prepare_redirect (&redirinfo, http_get_status_code (http),
- http_get_header (http, "Location"),
+ http_get_header (http, "Location", 0),
&request_buffer);
if (err)
goto leave;
@@ -1340,18 +1340,17 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
}
goto once_more;
- case 501:
- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
- goto leave;
-
- case 413: /* Payload too large */
- err = gpg_error (GPG_ERR_TOO_LARGE);
- goto leave;
-
default:
log_error (_("error accessing '%s': http status %u\n"),
request, http_get_status_code (http));
- err = gpg_error (GPG_ERR_NO_DATA);
+ switch (http_get_status_code (http))
+ {
+ case 401: err = gpg_error (GPG_ERR_NO_AUTH); break;
+ case 407: err = gpg_error (GPG_ERR_BAD_AUTH); break;
+ case 413: err = gpg_error (GPG_ERR_TOO_LARGE); break;
+ case 501: err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break;
+ default: err = gpg_error (GPG_ERR_NO_DATA); break;
+ }
goto leave;
}
diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c
index 3dca80ee6..5091ddf27 100644
--- a/dirmngr/ks-engine-http.c
+++ b/dirmngr/ks-engine-http.c
@@ -180,7 +180,7 @@ ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags,
{
xfree (request_buffer);
err = http_prepare_redirect (&redirinfo, http_get_status_code (http),
- http_get_header (http, "Location"),
+ http_get_header (http, "Location", 0),
&request_buffer);
if (err)
goto leave;
@@ -193,14 +193,16 @@ ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags,
}
goto once_more;
- case 413: /* Payload too large */
- err = gpg_error (GPG_ERR_TOO_LARGE);
- goto leave;
-
default:
log_error (_("error accessing '%s': http status %u\n"),
url, http_get_status_code (http));
- err = gpg_error (GPG_ERR_NO_DATA);
+ switch (http_get_status_code (http))
+ {
+ case 401: err = gpg_error (GPG_ERR_NO_AUTH); break;
+ case 407: err = gpg_error (GPG_ERR_BAD_AUTH); break;
+ case 413: err = gpg_error (GPG_ERR_TOO_LARGE); break;
+ default: err = gpg_error (GPG_ERR_NO_DATA); break;
+ }
goto leave;
}
diff --git a/dirmngr/ldap-misc.c b/dirmngr/ldap-misc.c
index 6b0939a3b..c3a659d5c 100644
--- a/dirmngr/ldap-misc.c
+++ b/dirmngr/ldap-misc.c
@@ -380,13 +380,14 @@ rfc4517toisotime (gnupg_isotime_t timebuf, const char *string)
int year, month, day, hour, minu, sec;
const char *s;
+ /* Sample value: "20230823141623Z"; */
for (i=0, s=string; i < 10; i++, s++) /* Need yyyymmddhh */
if (!digitp (s))
return gpg_error (GPG_ERR_INV_TIME);
year = atoi_4 (string);
month = atoi_2 (string + 4);
day = atoi_2 (string + 6);
- hour = atoi_2 (string + 9);
+ hour = atoi_2 (string + 8);
minu = 0;
sec = 0;
if (digitp (s) && digitp (s+1))
diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c
index 483b6f32d..ad7ed962a 100644
--- a/dirmngr/ocsp.c
+++ b/dirmngr/ocsp.c
@@ -227,7 +227,7 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp,
case 301:
case 302:
{
- const char *s = http_get_header (http, "Location");
+ const char *s = http_get_header (http, "Location", 0);
log_info (_("URL '%s' redirected to '%s' (%u)\n"),
url, s?s:"[none]", http_get_status_code (http));
diff --git a/dirmngr/server.c b/dirmngr/server.c
index ee61f63d6..1dbc87878 100644
--- a/dirmngr/server.c
+++ b/dirmngr/server.c
@@ -2202,6 +2202,7 @@ ensure_keyserver (ctrl_t ctrl)
uri_item_t plain_items = NULL;
uri_item_t ui;
strlist_t sl;
+ int none_seen = 1;
if (ctrl->server_local->keyservers)
return 0; /* Already set for this session. */
@@ -2214,6 +2215,15 @@ ensure_keyserver (ctrl_t ctrl)
for (sl = opt.keyserver; sl; sl = sl->next)
{
+ /* Frontends like Kleopatra may prefix option values without a
+ * scheme with "hkps://". Thus we need to check that too.
+ * Nobody will be mad enough to call a machine "none". */
+ if (!strcmp (sl->d, "none") || !strcmp (sl->d, "hkp://none")
+ || !strcmp (sl->d, "hkps://none"))
+ {
+ none_seen = 1;
+ continue;
+ }
err = make_keyserver_item (sl->d, &item);
if (err)
goto leave;
@@ -2229,6 +2239,12 @@ ensure_keyserver (ctrl_t ctrl)
}
}
+ if (none_seen && !plain_items && !onion_items)
+ {
+ err = gpg_error (GPG_ERR_NO_KEYSERVER);
+ goto leave;
+ }
+
/* Decide which to use. Note that the session has no keyservers
yet set. */
if (onion_items && !onion_items->next && plain_items && !plain_items->next)
@@ -2299,8 +2315,7 @@ cmd_keyserver (assuan_context_t ctx, char *line)
gpg_error_t err = 0;
int clear_flag, add_flag, help_flag, host_flag, resolve_flag;
int dead_flag, alive_flag;
- uri_item_t item = NULL; /* gcc 4.4.5 is not able to detect that it
- is always initialized. */
+ uri_item_t item = NULL;
clear_flag = has_option (line, "--clear");
help_flag = has_option (line, "--help");
@@ -2366,13 +2381,17 @@ cmd_keyserver (assuan_context_t ctx, char *line)
if (add_flag)
{
- err = make_keyserver_item (line, &item);
+ if (!strcmp (line, "none") || !strcmp (line, "hkp://none")
+ || !strcmp (line, "hkps://none"))
+ err = 0;
+ else
+ err = make_keyserver_item (line, &item);
if (err)
goto leave;
}
if (clear_flag)
release_ctrl_keyservers (ctrl);
- if (add_flag)
+ if (add_flag && item)
{
item->next = ctrl->server_local->keyservers;
ctrl->server_local->keyservers = item;
diff --git a/dirmngr/t-http.c b/dirmngr/t-http.c
index 7f3aa005d..3cc4be23a 100644
--- a/dirmngr/t-http.c
+++ b/dirmngr/t-http.c
@@ -288,6 +288,11 @@ main (int argc, char **argv)
my_http_flags |= HTTP_FLAG_FORCE_TOR;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--try-proxy"))
+ {
+ my_http_flags |= HTTP_FLAG_TRY_PROXY;
+ argc--; argv++;
+ }
else if (!strcmp (*argv, "--no-out"))
{
no_out = 1;
@@ -458,7 +463,7 @@ main (int argc, char **argv)
log_fatal ("http_get_header_names failed: %s\n",
gpg_strerror (gpg_error_from_syserror ()));
for (i = 0; names[i]; i++)
- printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i]));
+ printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i], 0));
xfree (names);
}
fflush (stdout);
@@ -484,7 +489,7 @@ main (int argc, char **argv)
case 301:
case 302:
case 307:
- log_info ("Redirected to: %s\n", http_get_header (hd, "Location"));
+ log_info ("Redirected to: %s\n", http_get_header (hd, "Location", 0));
break;
}
http_close (hd, 0);
diff --git a/doc/ad-query-hints.org b/doc/ad-query-hints.org
new file mode 100644
index 000000000..fd32a5831
--- /dev/null
+++ b/doc/ad-query-hints.org
@@ -0,0 +1,65 @@
+
+
+* Examples
+
+** List the DNs of all users in our QAUsers group
+
+: ad_query --subst --attr=dn
+: ^OU=QAUsers,$domain&sub&(&(objectcategory=person)(objectclass=user))
+
+** List the DN using the user's mail address
+
+: ad_query --subst --attr=dn,userAccountControl
+: (&(objectcategory=person)(objectclass=user)
+: (|(userPrincipalName=dd9jn@w32demo.g10code.de)
+: (mail=dd9jn@w32demo.g10code.de)))
+
+After that the userControlFlags should be checked - see below for
+the bit flags. For a non-disabled user use:
+
+: if ((userControlFlags & 0x0212) == 0x200))
+: use_this_user()
+
+
+* Useful attributes
+
+** userAccountControl
+
+These are bit flags. For details see
+https://learn.microsoft.com/en-us/windows/win32/api/iads/ne-iads-ads_user_flag_enum
+
+- 0x00000002 :: ADS_UF_ACCOUNTDISABLE, the account is disabled.
+- 0x00000010 :: ADS_UF_LOCKOUT, the account is temporarily locked out.
+- 0x00000100 :: ADS_UF_TEMP_DUPLICATE_ACCOUNT, this is an account for
+ a user whose primary account is in another domain.
+- 0x00000200 :: ADS_UF_NORMAL_ACCOUNT, the default account type that
+ represents a typical user.
+- 0x00000800 :: ADS_UF_INTERDOMAIN_TRUST_ACCOUNT, the account for a
+ domain-to-domain trust.
+- 0x00001000 :: ADS_UF_WORKSTATION_ACCOUNT, the computer account for a
+ computer that is a member of this domain.
+- 0x00002000 :: ADS_UF_SERVER_TRUST_ACCOUNT, the computer account for
+ a DC.
+- 0x00010000 :: ADS_UF_DONT_EXPIRE_PASSWD, the password will not expire.
+- 0x04000000 :: ADS_UF_PARTIAL_SECRETS_ACCOUNT, the computer account
+ for an RODC.
+
+For example to select only user accounts which are not disabled or
+are locked out could naivly be used:
+
+: (userAccountControl:1.2.840.113556.1.4.803:=512)
+
+1.2.840.113556.1.4.803 is bit wise AND, 1.2.840.113556.1.4.804 is bit
+wise OR. However, because a mask can't be specified, this is not really
+useful. Thus the above needs to be replaced by explicit checks; i.e.
+
+: (&(userAccountControl:1.2.840.113556.1.4.804:=512)
+: (!(userAccountControl:1.2.840.113556.1.4.804:=2))
+: (!(userAccountControl:1.2.840.113556.1.4.804:=16)))
+
+I'd suggest to also add explict checks on the returned data.
+
+
+* Resources
+
+- https://qa.social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
diff --git a/doc/dirmngr.texi b/doc/dirmngr.texi
index 8da4dcd37..84f568692 100644
--- a/doc/dirmngr.texi
+++ b/doc/dirmngr.texi
@@ -180,7 +180,7 @@ available flags the sole word "help" can be used.
This option is only useful for testing; it sets the system time back or
forth to @var{epoch} which is the number of seconds elapsed since the year
1970. Alternatively @var{epoch} may be given as a full ISO time string
-(e.g., "20070924T154812").
+(e.g. "20070924T154812").
@item --debug-level @var{level}
@opindex debug-level
@@ -344,7 +344,8 @@ whether Tor is locally running or not. The check for a running Tor is
done for each new connection.
If no keyserver is explicitly configured, dirmngr will use the
-built-in default of @code{https://keyserver.ubuntu.com}.
+built-in default of @code{https://keyserver.ubuntu.com}. To avoid the
+use of a default keyserver the value @code{none} can be used.
Windows users with a keyserver running on their Active Directory
may use the short form @code{ldap:///} for @var{name} to access this directory.
@@ -426,7 +427,9 @@ force the use of the default responder.
@item --honor-http-proxy
@opindex honor-http-proxy
If the environment variable @env{http_proxy} has been set, use its
-value to access HTTP servers.
+value to access HTTP servers. If on Windows the option is used but
+the environment variable is not set, the proxy settings are taken
+from the system.
@item --http-proxy @var{host}[:@var{port}]
@opindex http-proxy
diff --git a/doc/gpg.texi b/doc/gpg.texi
index 8203c1466..17de880ea 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -2715,6 +2715,12 @@ The available properties are:
second is the same but given as an ISO date string,
e.g., "2016-08-17". (drop-sig)
+ @item sig_expires
+ @itemx sig_expires_d
+ The expiration time of a signature packet or 0 if it does not
+ expire. The second is the same but given as an ISO date string or
+ an empty string e.g. "2038-01-19".
+
@item sig_algo
A number with the public key algorithm of a signature packet. (drop-sig)
diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi
index 602a39f50..0d4fb2fcc 100644
--- a/doc/gpgsm.texi
+++ b/doc/gpgsm.texi
@@ -732,6 +732,13 @@ instead to make sure that the gpgsm process exits with a failure if
the compliance rules are not fulfilled. Note that this option has
currently an effect only in "de-vs" mode.
+@item --always-trust
+@opindex always-trust
+Force encryption to the specified certificates without any validation
+of the certificate chain. The only requirement is that the
+certificate is capable of encryption. Note that this option is
+ineffective if @option{--require-compliance} is used.
+
@item --ignore-cert-with-oid @var{oid}
@opindex ignore-cert-with-oid
Add @var{oid} to the list of OIDs to be checked while reading
@@ -1621,6 +1628,10 @@ The leading two dashes usually used with @var{opt} shall not be given.
Return OK if the connection is in offline mode. This may be either
due to a @code{OPTION offline=1} or due to @command{gpgsm} being
started with option @option{--disable-dirmngr}.
+@item always-trust
+Returns OK of the connection is in always-trust mode. That is either
+@option{--always-trust} or @option{GPGSM OPTION always-trust} are
+active.
@end table
@node GPGSM OPTION
@@ -1727,6 +1738,15 @@ If @var{value} is true or @var{value} is not given all network access
is disabled for this session. This is the same as the command line
option @option{--disable-dirmngr}.
+@item always-trust
+If @var{value} is true or @var{value} is not given encryption to the
+specified certificates is forced without any validation of the
+certificate chain. The only requirement is that the certificates are
+capable of encryption. If set to false the standard behaviour is
+re-established. This option is cleared by a RESET and after each
+encrypt operation. Note that this option is ignored if
+@option{--always-trust} or @option{--require-compliance} are used.
+
@item input-size-hint
This is the same as the @option{--input-size-hint} command line option.
diff --git a/g10/call-agent.c b/g10/call-agent.c
index d6e4575c3..cb7053396 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -130,6 +130,7 @@ status_sc_op_failure (int rc)
write_status_text (STATUS_SC_OP_FAILURE, "1");
break;
case GPG_ERR_BAD_PIN:
+ case GPG_ERR_BAD_RESET_CODE:
write_status_text (STATUS_SC_OP_FAILURE, "2");
break;
default:
diff --git a/g10/card-util.c b/g10/card-util.c
index 631f48d9d..088ea824a 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -56,6 +56,7 @@ write_sc_op_status (gpg_error_t err)
write_status_text (STATUS_SC_OP_FAILURE, "1");
break;
case GPG_ERR_BAD_PIN:
+ case GPG_ERR_BAD_RESET_CODE:
write_status_text (STATUS_SC_OP_FAILURE, "2");
break;
default:
diff --git a/g10/gpg.c b/g10/gpg.c
index ec6af0eb9..96a0c345c 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -3486,7 +3486,7 @@ main (int argc, char **argv)
break;
case oUtf8Strings: utf8_strings = 1; break;
case oNoUtf8Strings:
-#ifdef HAVE_W32_SYSTEM
+#ifndef HAVE_W32_SYSTEM
utf8_strings = 0;
#endif
break;
diff --git a/g10/import.c b/g10/import.c
index d84a083cc..c1e76c3f0 100644
--- a/g10/import.c
+++ b/g10/import.c
@@ -1509,6 +1509,20 @@ impex_filter_getval (void *cookie, const char *propname)
{
result = dateonlystr_from_sig (sig);
}
+ else if (!strcmp (propname, "sig_expires"))
+ {
+ snprintf (numbuf, sizeof numbuf, "%lu", (ulong)sig->expiredate);
+ result = numbuf;
+ }
+ else if (!strcmp (propname, "sig_expires_d"))
+ {
+ static char exdatestr[MK_DATESTR_SIZE];
+
+ if (sig->expiredate)
+ result = mk_datestr (exdatestr, sizeof exdatestr, sig->expiredate);
+ else
+ result = "";
+ }
else if (!strcmp (propname, "sig_algo"))
{
snprintf (numbuf, sizeof numbuf, "%d", sig->pubkey_algo);
diff --git a/g10/keygen.c b/g10/keygen.c
index 1605bff89..9b0113c5a 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -2748,6 +2748,7 @@ parse_expire_string_with_ct (const char *string, u32 creation_time)
u32 seconds;
u32 abs_date = 0;
time_t tt;
+ uint64_t tmp64;
u32 curtime;
if (creation_time == (u32)-1)
@@ -2759,14 +2760,26 @@ parse_expire_string_with_ct (const char *string, u32 creation_time)
|| !strcmp (string, "never") || !strcmp (string, "-"))
seconds = 0;
else if (!strncmp (string, "seconds=", 8))
- seconds = atoi (string+8);
+ seconds = scan_secondsstr (string+8);
else if ((abs_date = scan_isodatestr(string))
&& (abs_date+86400/2) > curtime)
seconds = (abs_date+86400/2) - curtime;
- else if ((tt = isotime2epoch (string)) != (time_t)(-1))
- seconds = (u32)tt - curtime;
+ else if ((tt = isotime2epoch_u64 (string)) != (uint64_t)(-1))
+ {
+ tmp64 = tt - curtime;
+ if (tmp64 >= (u32)(-1))
+ seconds = (u32)(-1) - 1; /* cap value. */
+ else
+ seconds = (u32)tmp64;
+ }
else if ((mult = check_valid_days (string)))
- seconds = atoi (string) * 86400L * mult;
+ {
+ tmp64 = scan_secondsstr (string) * 86400L * mult;
+ if (tmp64 >= (u32)(-1))
+ seconds = (u32)(-1) - 1; /* cap value. */
+ else
+ seconds = (u32)tmp64;
+ }
else
seconds = (u32)(-1);
@@ -2790,11 +2803,16 @@ parse_creation_string (const char *string)
if (!*string)
seconds = 0;
else if ( !strncmp (string, "seconds=", 8) )
- seconds = atoi (string+8);
+ seconds = scan_secondsstr (string+8);
else if ( !(seconds = scan_isodatestr (string)))
{
- time_t tmp = isotime2epoch (string);
- seconds = (tmp == (time_t)(-1))? 0 : tmp;
+ uint64_t tmp = isotime2epoch_u64 (string);
+ if (tmp == (uint64_t)(-1))
+ seconds = 0;
+ else if (tmp > (u32)(-1))
+ seconds = 0;
+ else
+ seconds = tmp;
}
return seconds;
}
@@ -5395,17 +5413,26 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk,
{
ecdh_param_str = ecdh_param_str_from_pk (sk);
if (!ecdh_param_str)
- return gpg_error_from_syserror ();
+ {
+ free_public_key (sk);
+ return gpg_error_from_syserror ();
+ }
}
err = hexkeygrip_from_pk (sk, &hexgrip);
if (err)
- goto leave;
+ {
+ xfree (ecdh_param_str);
+ free_public_key (sk);
+ goto leave;
+ }
memset(&info, 0, sizeof (info));
rc = agent_scd_getattr ("SERIALNO", &info);
if (rc)
{
+ xfree (ecdh_param_str);
+ free_public_key (sk);
err = (gpg_error_t)rc;
goto leave;
}
diff --git a/g10/trustdb.c b/g10/trustdb.c
index 051a534f9..e846abe82 100644
--- a/g10/trustdb.c
+++ b/g10/trustdb.c
@@ -730,7 +730,7 @@ tdb_check_or_update (ctrl_t ctrl)
if (opt.interactive)
update_trustdb (ctrl);
else if (!opt.no_auto_check_trustdb)
- check_trustdb (ctrl);
+ check_trustdb (ctrl);
}
}
@@ -983,6 +983,7 @@ update_min_ownertrust (ctrl_t ctrl, u32 *kid, unsigned int new_trust)
/*
* Clear the ownertrust and min_ownertrust values.
+ * Also schedule a revalidation if a stale validity record exists.
*
* Return: True if a change actually happened.
*/
@@ -1016,6 +1017,26 @@ tdb_clear_ownertrusts (ctrl_t ctrl, PKT_public_key *pk)
do_sync ();
return 1;
}
+ else
+ {
+ /* Check whether we have a stale RECTYPE_VALID for that key
+ * and if its validity ist set, schedule a revalidation. */
+ ulong recno = rec.r.trust.validlist;
+ while (recno)
+ {
+ read_record (recno, &rec, RECTYPE_VALID);
+ if (rec.r.valid.validity)
+ break;
+ recno = rec.r.valid.next;
+ }
+ if (recno)
+ {
+ if (DBG_TRUST)
+ log_debug ("stale validity value detected"
+ " - scheduling check\n");
+ tdb_revalidation_mark (ctrl);
+ }
+ }
}
else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
{
diff --git a/m4/autobuild.m4 b/m4/autobuild.m4
index ceed46494..20086ae9f 100644
--- a/m4/autobuild.m4
+++ b/m4/autobuild.m4
@@ -23,6 +23,9 @@ AC_DEFUN([AB_INIT],
if test "$hostname"; then
AC_MSG_NOTICE([autobuild hostname... $hostname])
fi
+ if test "$EMAIL"; then
+ AC_MSG_NOTICE([autobuild username... $EMAIL])
+ fi
ifelse([$1],[],,[AC_MSG_NOTICE([autobuild mode... $1])])
date=`date +%Y%m%d-%H%M%S`
if test "$?" != 0; then
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 66ec9f4a9..fd9ce554c 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -3453,7 +3453,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
if (!remaining)
{
log_error (_("Reset Code not or not anymore available\n"));
- rc = gpg_error (GPG_ERR_BAD_PIN);
+ rc = gpg_error (GPG_ERR_NO_RESET_CODE);
goto leave;
}
@@ -3470,7 +3470,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
log_info (_("Reset Code is too short; minimum length is %d\n"),
minlen);
- rc = gpg_error (GPG_ERR_BAD_PIN);
+ rc = gpg_error (GPG_ERR_BAD_RESET_CODE);
goto leave;
}
}
@@ -3538,7 +3538,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
if (bufferlen != 0 && bufferlen < 8)
{
log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
- rc = gpg_error (GPG_ERR_BAD_PIN);
+ rc = gpg_error (GPG_ERR_BAD_RESET_CODE);
}
else
{
diff --git a/sm/certchain.c b/sm/certchain.c
index 84dbed696..9d0fe684b 100644
--- a/sm/certchain.c
+++ b/sm/certchain.c
@@ -2199,9 +2199,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime,
memset (&rootca_flags, 0, sizeof rootca_flags);
- rc = do_validate_chain (ctrl, cert, checktime,
- r_exptime, listmode, listfp, flags,
- &rootca_flags);
+ if ((flags & VALIDATE_FLAG_BYPASS))
+ {
+ *retflags |= VALIDATE_FLAG_BYPASS;
+ rc = 0;
+ }
+ else
+ rc = do_validate_chain (ctrl, cert, checktime,
+ r_exptime, listmode, listfp, flags,
+ &rootca_flags);
if (!rc && (flags & VALIDATE_FLAG_STEED))
{
*retflags |= VALIDATE_FLAG_STEED;
@@ -2223,6 +2229,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime,
if (opt.verbose)
do_list (0, listmode, listfp, _("validation model used: %s"),
+ (*retflags & VALIDATE_FLAG_BYPASS)?
+ "bypass" :
(*retflags & VALIDATE_FLAG_STEED)?
"steed" :
(*retflags & VALIDATE_FLAG_CHAIN_MODEL)?
diff --git a/sm/certlist.c b/sm/certlist.c
index fdf31a198..53d90ac30 100644
--- a/sm/certlist.c
+++ b/sm/certlist.c
@@ -448,6 +448,11 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
if (!rc && !is_cert_in_certlist (cert, *listaddr))
{
+ unsigned int valflags = 0;
+
+ if (!secret && (opt.always_trust || ctrl->always_trust))
+ valflags |= VALIDATE_FLAG_BYPASS;
+
if (!rc && secret)
{
char *p;
@@ -461,9 +466,10 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
xfree (p);
}
}
+
if (!rc)
rc = gpgsm_validate_chain (ctrl, cert, GNUPG_ISOTIME_NONE, NULL,
- 0, NULL, 0, NULL);
+ 0, NULL, valflags, NULL);
if (!rc)
{
certlist_t cl = xtrycalloc (1, sizeof *cl);
diff --git a/sm/decrypt.c b/sm/decrypt.c
index a30eafc55..6121fd278 100644
--- a/sm/decrypt.c
+++ b/sm/decrypt.c
@@ -1064,6 +1064,7 @@ gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp)
KEYDB_HANDLE kh;
int recp;
struct decrypt_filter_parm_s dfparm;
+ char *curve = NULL;
memset (&dfparm, 0, sizeof dfparm);
@@ -1300,14 +1301,15 @@ gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp)
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
- pk_algo = gpgsm_get_key_algo_info (cert, &nbits);
+ xfree (curve);
+ pk_algo = gpgsm_get_key_algo_info (cert, &nbits, &curve);
if (!opt.quiet)
log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr);
/* Check compliance. */
if (!gnupg_pk_is_allowed (opt.compliance,
PK_USE_DECRYPTION,
- pk_algo, 0, NULL, nbits, NULL))
+ pk_algo, 0, NULL, nbits, curve))
{
char kidstr[10+1];
@@ -1325,7 +1327,7 @@ gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp)
dfparm.is_de_vs =
(dfparm.is_de_vs
&& gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0,
- NULL, nbits, NULL));
+ NULL, nbits, curve));
oops:
if (rc)
@@ -1503,6 +1505,7 @@ gpgsm_decrypt (ctrl_t ctrl, estream_t in_fp, estream_t out_fp)
log_error ("message decryption failed: %s <%s>\n",
gpg_strerror (rc), gpg_strsource (rc));
}
+ xfree (curve);
ksba_cms_release (cms);
gnupg_ksba_destroy_reader (b64reader);
gnupg_ksba_destroy_writer (b64writer);
diff --git a/sm/encrypt.c b/sm/encrypt.c
index 923fdfd99..9113028db 100644
--- a/sm/encrypt.c
+++ b/sm/encrypt.c
@@ -749,11 +749,12 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp,
unsigned char *encval;
unsigned int nbits;
int pk_algo;
+ char *curve = NULL;
/* Check compliance. */
- pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits);
+ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits, &curve);
if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, 0,
- NULL, nbits, NULL))
+ NULL, nbits, curve))
{
char kidstr[10+1];
@@ -768,9 +769,12 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, estream_t data_fp,
/* Fixme: When adding ECC we need to provide the curvename and
* the key to gnupg_pk_is_compliant. */
if (compliant
- && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL))
+ && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, curve))
compliant = 0;
+ xfree (curve);
+ curve = NULL;
+
rc = encrypt_dek (dek, cl->cert, pk_algo, &encval);
if (rc)
{
diff --git a/sm/export.c b/sm/export.c
index 54893b54d..a6ba40f5d 100644
--- a/sm/export.c
+++ b/sm/export.c
@@ -430,7 +430,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode)
if (rawmode == 0)
ctrl->pem_name = "PKCS12";
- else if (gpgsm_get_key_algo_info (cert, NULL) == GCRY_PK_ECC)
+ else if (gpgsm_get_key_algo_info (cert, NULL, NULL) == GCRY_PK_ECC)
ctrl->pem_name = "EC PRIVATE KEY";
else if (rawmode == 1)
ctrl->pem_name = "PRIVATE KEY";
diff --git a/sm/fingerprint.c b/sm/fingerprint.c
index 5f3f6f51f..375a8647e 100644
--- a/sm/fingerprint.c
+++ b/sm/fingerprint.c
@@ -222,7 +222,7 @@ gpgsm_get_keygrip_hexstring (ksba_cert_t cert)
* algorithm is used the name or OID of the curve is stored there; the
* caller needs to free this value. */
int
-gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve)
+gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits, char **r_curve)
{
gcry_sexp_t s_pkey;
int rc;
@@ -299,18 +299,11 @@ gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits, char **r_curve)
}
-int
-gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits)
-{
- return gpgsm_get_key_algo_info2 (cert, nbits, NULL);
-}
-
-
/* Return true if CERT is an ECC key. */
int
gpgsm_is_ecc_key (ksba_cert_t cert)
{
- return GCRY_PK_ECC == gpgsm_get_key_algo_info2 (cert, NULL, NULL);
+ return GCRY_PK_ECC == gpgsm_get_key_algo_info (cert, NULL, NULL);
}
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 74d23e5c1..4b6c353a0 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -215,6 +215,7 @@ enum cmd_and_opt_values {
oRequireCompliance,
oCompatibilityFlags,
oKbxBufferSize,
+ oAlwaysTrust,
oNoAutostart
};
@@ -417,6 +418,7 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"),
ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"),
ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"),
+ ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"),
ARGPARSE_header (NULL, N_("Options for unattended use")),
@@ -1494,6 +1496,7 @@ main ( int argc, char **argv)
case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break;
case oRequireCompliance: opt.require_compliance = 1; break;
+ case oAlwaysTrust: opt.always_trust = 1; break;
case oKbxBufferSize:
keybox_set_buffersize (pargs.r.ret_ulong, 0);
@@ -1583,10 +1586,20 @@ main ( int argc, char **argv)
if (may_coredump && !opt.quiet)
log_info (_("WARNING: program may create a core file!\n"));
+ if (opt.require_compliance && opt.always_trust)
+ {
+ opt.always_trust = 0;
+ if (opt.quiet)
+ log_info (_("WARNING: %s overrides %s\n"),
+ "--require-compliance","--always-trust");
+ }
+
+
npth_init ();
gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
assuan_control (ASSUAN_CONTROL_REINIT_SYSCALL_CLAMP, NULL);
+
/* if (opt.qualsig_approval && !opt.quiet) */
/* log_info (_("This software has officially been approved to " */
/* "create and verify\n" */
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index 93a80631f..eb9ba9f17 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -116,8 +116,6 @@ struct
int extra_digest_algo; /* A digest algorithm also used for
verification of signatures. */
- int always_trust; /* Trust the given keys even if there is no
- valid certification chain */
int skip_verify; /* do not check signatures on data */
int lock_once; /* Keep lock once they are set */
@@ -164,6 +162,10 @@ struct
* mode. */
int require_compliance;
+ /* Enable always-trust mode - note that there is also server option
+ * for this. */
+ int always_trust;
+
/* Enable creation of authenticode signatures. */
int authenticode;
@@ -269,6 +271,9 @@ struct server_control_s
2 := STEED model. */
int offline; /* If true gpgsm won't do any network access. */
+ int always_trust; /* True in always-trust mode; see also
+ * opt.always-trust. */
+
/* The current time. Used as a helper in certchain.c. */
ksba_isotime_t current_time;
@@ -334,9 +339,8 @@ unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert,
unsigned long *r_high);
unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array);
char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert);
-int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits);
-int gpgsm_get_key_algo_info2 (ksba_cert_t cert, unsigned int *nbits,
- char **r_curve);
+int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits,
+ char **r_curve);
int gpgsm_is_ecc_key (ksba_cert_t cert);
char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid);
gcry_mpi_t gpgsm_get_rsa_modulus (ksba_cert_t cert);
@@ -388,6 +392,7 @@ int gpgsm_create_cms_signature (ctrl_t ctrl,
#define VALIDATE_FLAG_NO_DIRMNGR 1
#define VALIDATE_FLAG_CHAIN_MODEL 2
#define VALIDATE_FLAG_STEED 4
+#define VALIDATE_FLAG_BYPASS 8 /* No actual validation. */
gpg_error_t gpgsm_walk_cert_chain (ctrl_t ctrl,
ksba_cert_t start, ksba_cert_t *r_next);
diff --git a/sm/keylist.c b/sm/keylist.c
index fabd82224..ed1b74729 100644
--- a/sm/keylist.c
+++ b/sm/keylist.c
@@ -54,7 +54,7 @@ struct list_external_parm_s
#define OID_FLAG_SKIP 1
/* The extension is a simple UTF8String and should be printed. */
#define OID_FLAG_UTF8 2
-/* The extension can be trnted as a hex string. */
+/* The extension can be printed as a hex string. */
#define OID_FLAG_HEX 4
/* Define if this specififies a key purpose. */
#define OID_FLAG_KP 8
@@ -208,6 +208,8 @@ static struct
{ "1.3.6.1.4.1.311.21.6", "ms-keyRecovery", OID_FLAG_KP },
{ "1.3.6.1.4.1.311.21.19", "ms-dsEmailReplication", OID_FLAG_KP },
+ /* BSI policies. */
+
/* Other vendor extensions. */
{ "1.3.6.1.4.1.30205.13.1.1", "trusted-disk", OID_FLAG_KP },
{ "1.2.840.113583.1.1.5", "pdfAuthenticDocumentsTrust", OID_FLAG_KP },
@@ -428,7 +430,7 @@ email_kludge (const char *name)
* number. NBITS is the length of the key in bits. */
static void
print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits,
- estream_t fp)
+ const char *curvename, estream_t fp)
{
int indent = 0;
int hashalgo;
@@ -436,7 +438,7 @@ print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits,
/* Note that we do not need to test for PK_ALGO_FLAG_RSAPSS because
* that is not a property of the key but one of the created
* signature. */
- if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, NULL))
+ if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, curvename))
{
hashalgo = gcry_md_map_name (ksba_cert_get_digest_algo (cert));
if (gnupg_digest_is_compliant (CO_DE_VS, hashalgo))
@@ -560,7 +562,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
if (*truststring)
es_fputs (truststring, fp);
- algo = gpgsm_get_key_algo_info2 (cert, &nbits, &curve);
+ algo = gpgsm_get_key_algo_info (cert, &nbits, &curve);
es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
ksba_cert_get_validity (cert, 0, t);
@@ -627,7 +629,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
if (curve)
es_fputs (curve, fp);
es_putc (':', fp); /* End of field 17. */
- print_compliance_flags (cert, algo, nbits, fp);
+ print_compliance_flags (cert, algo, nbits, curve, fp);
es_putc (':', fp); /* End of field 18. */
es_putc ('\n', fp);
diff --git a/sm/minip12.c b/sm/minip12.c
index 265243f3e..ae81d821b 100644
--- a/sm/minip12.c
+++ b/sm/minip12.c
@@ -50,6 +50,8 @@
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
#endif
+/* Enable the next macro to dump stuff for debugging. */
+#undef ENABLE_DER_STRUCT_DUMPING
static unsigned char const oid_data[9] = {
@@ -81,6 +83,17 @@ static unsigned char const oid_aes128_CBC[9] = {
static unsigned char const oid_aes256_CBC[9] = {
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2A };
+static unsigned char const oid_hmacWithSHA1[8] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07 };
+static unsigned char const oid_hmacWithSHA224[8] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x08 };
+static unsigned char const oid_hmacWithSHA256[8] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x09 };
+static unsigned char const oid_hmacWithSHA384[8] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0A };
+static unsigned char const oid_hmacWithSHA512[8] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0B };
+
static unsigned char const oid_rsaEncryption[9] = {
0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
static unsigned char const oid_pcPublicKey[7] = {
@@ -111,6 +124,8 @@ static unsigned char const data_mactemplate[51] = {
#define DATA_MACTEMPLATE_MAC_OFF 17
#define DATA_MACTEMPLATE_SALT_OFF 39
+/* Note that the BMP String in this template reads:
+ * "GnuPG exported certificate ffffffff" */
static unsigned char const data_attrtemplate[106] = {
0x31, 0x7c, 0x30, 0x55, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31,
@@ -135,55 +150,6 @@ struct buffer_s
};
-struct tag_info
-{
- int class;
- int is_constructed;
- unsigned long tag;
- size_t length; /* length part of the TLV */
- size_t nhdr;
- int ndef; /* It is an indefinite length */
-};
-
-
-#define TLV_MAX_DEPTH 20
-
-
-struct bufferlist_s
-{
- struct bufferlist_s *next;
- char *buffer;
-};
-
-
-/* An object to control the ASN.1 parsing. */
-struct tlv_ctx_s
-{
- /* The current buffer we are working on and its length. */
- const unsigned char *buffer;
- size_t bufsize;
-
- size_t offset; /* The current offset into this buffer. */
- int in_ndef; /* Flag indicating that we are in a NDEF. */
- int pending; /* The last tlv_next has not yet been processed. */
-
- struct tag_info ti; /* The current tag. */
- gpg_error_t lasterr; /* Last error from tlv function. */
- const char *lastfunc;/* Name of last called function. */
-
- struct bufferlist_s *bufferlist; /* To keep track of amlloced buffers. */
-
- unsigned int pop_count;/* Number of pops by tlv_next. */
- unsigned int stacklen; /* Used size of the stack. */
- struct {
- const unsigned char *buffer; /* Saved value of BUFFER. */
- size_t bufsize; /* Saved value of BUFSIZE. */
- size_t offset; /* Saved value of OFFSET. */
- int in_ndef; /* Saved IN_NDEF flag. */
- } stack[TLV_MAX_DEPTH];
-};
-
-
/* Parser communication object. */
struct p12_parse_ctx_s
{
@@ -208,9 +174,6 @@ struct p12_parse_ctx_s
static int opt_verbose;
-static unsigned char *cram_octet_string (const unsigned char *input,
- size_t length, size_t *r_newlength);
-
@@ -223,15 +186,29 @@ p12_set_verbosity (int verbose, int debug)
}
-static void
-dump_tag_info (const char *text, struct tag_info *ti)
+static int
+digest_algo_from_oid (unsigned char const *oid, size_t oidlen)
{
- if (opt_verbose > 1)
- log_debug ("p12_parse(%s): ti.class=%d tag=%lu len=%zu nhdr=%zu %s%s\n",
- text,
- ti->class, ti->tag, ti->length, ti->nhdr,
- ti->is_constructed?" cons":"",
- ti->ndef?" ndef":"");
+ int algo;
+
+ if (oidlen == DIM(oid_hmacWithSHA1) &&
+ !memcmp (oid, oid_hmacWithSHA1, oidlen))
+ algo = GCRY_MD_SHA1;
+ else if (oidlen == DIM(oid_hmacWithSHA224) &&
+ !memcmp (oid, oid_hmacWithSHA224, oidlen))
+ algo = GCRY_MD_SHA224;
+ else if (oidlen == DIM(oid_hmacWithSHA256) &&
+ !memcmp (oid, oid_hmacWithSHA256, oidlen))
+ algo = GCRY_MD_SHA256;
+ else if (oidlen == DIM(oid_hmacWithSHA384) &&
+ !memcmp (oid, oid_hmacWithSHA384, oidlen))
+ algo = GCRY_MD_SHA384;
+ else if (oidlen == DIM(oid_hmacWithSHA512) &&
+ !memcmp (oid, oid_hmacWithSHA512, oidlen))
+ algo = GCRY_MD_SHA512;
+ else
+ algo = 0;
+ return algo;
}
@@ -327,538 +304,6 @@ builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi,
}
-
-/* Parse the buffer at the address BUFFER which is of SIZE and return
- * the tag and the length part from the TLV triplet. Update BUFFER
- * and SIZE on success. Checks that the encoded length does not
- * exhaust the length of the provided buffer. */
-static int
-parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti)
-{
- gpg_error_t err;
- int tag;
-
- err = parse_ber_header (buffer, size,
- &ti->class, &tag,
- &ti->is_constructed, &ti->ndef,
- &ti->length, &ti->nhdr);
- if (err)
- return err;
- if (tag < 0)
- return gpg_error (GPG_ERR_EOVERFLOW);
- ti->tag = tag;
-
- if (ti->length > *size)
- return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); /* data larger than buffer. */
-
- return 0;
-}
-
-
-/* Create a new TLV object. */
-static struct tlv_ctx_s *
-tlv_new (const unsigned char *buffer, size_t bufsize)
-{
- struct tlv_ctx_s *tlv;
- tlv = xtrycalloc (1, sizeof *tlv);
- if (tlv)
- {
- tlv->buffer = buffer;
- tlv->bufsize = bufsize;
- }
- return tlv;
-}
-
-
-/* This function can be used to store a malloced buffer into the TLV
- * object. Ownership of BUFFER is thus transferred to TLV. This
- * buffer will then only be released by tlv_release. */
-static gpg_error_t
-tlv_register_buffer (struct tlv_ctx_s *tlv, char *buffer)
-{
- struct bufferlist_s *item;
-
- item = xtrycalloc (1, sizeof *item);
- if (!item)
- return gpg_error_from_syserror ();
- item->buffer = buffer;
- item->next = tlv->bufferlist;
- tlv->bufferlist = item;
- return 0;
-}
-
-
-static void
-tlv_release (struct tlv_ctx_s *tlv)
-{
- if (!tlv)
- return;
- while (tlv->bufferlist)
- {
- struct bufferlist_s *save = tlv->bufferlist->next;
- xfree (tlv->bufferlist->buffer);
- xfree (tlv->bufferlist);
- tlv->bufferlist = save;
- }
- xfree (tlv);
-}
-
-
-/* Helper for tlv_next and tlv_peek. */
-static gpg_error_t
-_tlv_peek (struct tlv_ctx_s *tlv, size_t *r_n)
-{
- const unsigned char *p;
-
- if (tlv->offset > tlv->bufsize)
- return gpg_error (GPG_ERR_BUG);
- p = tlv->buffer + tlv->offset;
- *r_n = tlv->bufsize - tlv->offset;
- return parse_tag (&p, r_n, &tlv->ti);
-}
-
-
-/* Helper for tlv_expect_sequence and tlv_expect_context_tag. */
-static gpg_error_t
-_tlv_push (struct tlv_ctx_s *tlv)
-{
- if (tlv->stacklen >= TLV_MAX_DEPTH)
- return (tlv->lasterr = gpg_error (GPG_ERR_TOO_MANY));
- tlv->stack[tlv->stacklen].buffer = tlv->buffer;
- tlv->stack[tlv->stacklen].bufsize = tlv->bufsize;
- tlv->stack[tlv->stacklen].offset = tlv->offset;
- tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef;
- tlv->stacklen++;
- tlv->buffer += tlv->offset;
- tlv->bufsize = tlv->ti.length;
- tlv->offset = 0;
- tlv->in_ndef = tlv->ti.ndef;
- return 0;
-}
-
-
-/* Helper for tlv_next. */
-static gpg_error_t
-_tlv_pop (struct tlv_ctx_s *tlv)
-{
- size_t saveoff;
-
- if (!tlv->stacklen)
- return gpg_error (GPG_ERR_EOF);
-
- saveoff = tlv->offset;
-
- tlv->stacklen--;
- tlv->buffer = tlv->stack[tlv->stacklen].buffer;
- tlv->bufsize = tlv->stack[tlv->stacklen].bufsize;
- tlv->offset = tlv->stack[tlv->stacklen].offset;
- tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef;
-
- /* Move offset of the container to the end of the container. */
- tlv->offset += saveoff;
- if (tlv->offset > tlv->bufsize)
- return gpg_error (GPG_ERR_INV_BER);
-
- tlv->pop_count++;
- return 0;
-}
-
-
-/* Parse the next tag and value. Also detect the end of a container;
- * tlv_popped() can be used to detect this. */
-static gpg_error_t
-tlv_next (struct tlv_ctx_s *tlv)
-{
- gpg_error_t err;
- size_t n;
-
- tlv->pop_count = 0;
- tlv->lasterr = 0;
- tlv->lastfunc = __func__;
- if (tlv->pending)
- {
- tlv->pending = 0;
- return 0;
- }
-
- if (!tlv->in_ndef && tlv->offset == tlv->bufsize)
- {
- /* We are at the end of a container. Pop the stack. */
- do
- err = _tlv_pop (tlv);
- while (!err && !tlv->in_ndef && tlv->offset == tlv->bufsize);
- if (err)
- return (tlv->lasterr = err);
- }
-
- err = _tlv_peek (tlv, &n);
- if (err)
- return err;
- if (tlv->in_ndef && (tlv->ti.class == CLASS_UNIVERSAL
- && !tlv->ti.tag && !tlv->ti.is_constructed))
- {
- /* End tag while in ndef container. Skip the tag, and pop. */
- tlv->offset += n - (tlv->bufsize - tlv->offset);
- err = _tlv_pop (tlv);
- /* FIXME: We need to peek whether there is another end tag and
- * pop again. We can't modify the TLV object, though. */
- if (err)
- return (tlv->lasterr = err);
- }
-
- /* Set offset to the value of the TLV. */
- tlv->offset += tlv->bufsize - tlv->offset - n;
- dump_tag_info ("tlv_next", &tlv->ti);
- return 0;
-}
-
-
-/* Return the current neting level of the TLV object. */
-static unsigned int
-tlv_level (struct tlv_ctx_s *tlv)
-{
- return tlv->stacklen;
-}
-
-
-/* If called right after tlv_next the number of container levels
- * popped are returned. */
-static unsigned int
-tlv_popped (struct tlv_ctx_s *tlv)
-{
- return tlv->pop_count;
-}
-
-
-/* Set a flag to indicate that the last tlv_next has not yet been
- * consumed. */
-static void
-tlv_set_pending (struct tlv_ctx_s *tlv)
-{
- tlv->pending = 1;
-}
-
-
-/* Skip over the value of the current tag. */
-static void
-tlv_skip (struct tlv_ctx_s *tlv)
-{
- tlv->lastfunc = __func__;
- tlv->offset += tlv->ti.length;
-}
-
-
-/* Expect that the current tag is a sequence and setup the context for
- * processing. */
-static gpg_error_t
-tlv_expect_sequence (struct tlv_ctx_s *tlv)
-{
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE
- && tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- return _tlv_push (tlv);
-}
-
-/* Variant of tlv_expect_sequence to be used for the ouyter sequence
- * of an object which might have padding after the ASN.1 data. */
-static gpg_error_t
-tlv_expect_top_sequence (struct tlv_ctx_s *tlv)
-{
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE
- && tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- tlv->bufsize = tlv->ti.nhdr + tlv->ti.length;
- return _tlv_push (tlv);
-}
-
-
-/* Expect that the current tag is a context tag and setup the context
- * for processing. The tag of the context is returned at R_TAG. */
-static gpg_error_t
-tlv_expect_context_tag (struct tlv_ctx_s *tlv, int *r_tag)
-{
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_CONTEXT && tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- *r_tag = tlv->ti.tag;
- return _tlv_push (tlv);
-}
-
-
-/* Expect that the current tag is a SET and setup the context for
- * processing. */
-static gpg_error_t
-tlv_expect_set (struct tlv_ctx_s *tlv)
-{
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SET
- && tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- return _tlv_push (tlv);
-}
-
-
-/* Expect an object of CLASS with TAG and store its value at
- * (R_DATA,R_DATALEN). Then skip over its value to the next tag.
- * Note that the stored value is not allocated but points into
- * TLV. */
-static gpg_error_t
-tlv_expect_object (struct tlv_ctx_s *tlv, int class, int tag,
- unsigned char const **r_data, size_t *r_datalen)
-{
- gpg_error_t err;
- const unsigned char *p;
-
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == class && tlv->ti.tag == tag))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- p = tlv->buffer + tlv->offset;
- if (!tlv->ti.length)
- return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
-
- if (class == CLASS_CONTEXT && tag == 0 && tlv->ti.is_constructed)
- {
- char *newbuffer;
-
- newbuffer = cram_octet_string (p, tlv->ti.length, r_datalen);
- if (!newbuffer)
- return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER));
- err = tlv_register_buffer (tlv, newbuffer);
- if (err)
- {
- xfree (newbuffer);
- return (tlv->lasterr = err);
- }
- *r_data = newbuffer;
- }
- else
- {
- *r_data = p;
- *r_datalen = tlv->ti.length;
- }
-
- tlv->offset += tlv->ti.length;
- return 0;
-}
-
-
-/* Expect that the current tag is an object string and store its value
- * at (R_DATA,R_DATALEN). Then skip over its value to the next tag.
- * Note that the stored value are not allocated but point into TLV.
- * If ENCAPSULATES is set the octet string is used as a new
- * container. R_DATA and R_DATALEN are optional. */
-static gpg_error_t
-tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates,
- unsigned char const **r_data, size_t *r_datalen)
-{
- gpg_error_t err;
- const unsigned char *p;
- size_t n;
-
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING
- && (!tlv->ti.is_constructed || encapsulates)))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- p = tlv->buffer + tlv->offset;
- if (!(n=tlv->ti.length))
- return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
-
- if (encapsulates && tlv->ti.is_constructed)
- {
- char *newbuffer;
-
- newbuffer = cram_octet_string (p, n, r_datalen);
- if (!newbuffer)
- return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER));
- err = tlv_register_buffer (tlv, newbuffer);
- if (err)
- {
- xfree (newbuffer);
- return (tlv->lasterr = err);
- }
- *r_data = newbuffer;
- }
- else
- {
- if (r_data)
- *r_data = p;
- if (r_datalen)
- *r_datalen = tlv->ti.length;
- }
- if (encapsulates)
- return _tlv_push (tlv);
-
- tlv->offset += tlv->ti.length;
- return 0;
-}
-
-
-/* Expect a NULL tag. */
-static gpg_error_t
-tlv_expect_null (struct tlv_ctx_s *tlv)
-{
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_NULL
- && !tlv->ti.is_constructed && !tlv->ti.length))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- return 0;
-}
-
-
-/* Expect that the current tag is an integer and return its value at
- * R_VALUE. Then skip over its value to the next tag. */
-static gpg_error_t
-tlv_expect_integer (struct tlv_ctx_s *tlv, int *r_value)
-{
- const unsigned char *p;
- size_t n;
- int value;
-
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER
- && !tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- p = tlv->buffer + tlv->offset;
- if (!(n=tlv->ti.length))
- return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
-
- /* We currently support only positive values. */
- if ((*p & 0x80))
- return (tlv->lasterr = gpg_error (GPG_ERR_ERANGE));
-
- for (value = 0; n; n--)
- {
- value <<= 8;
- value |= (*p++) & 0xff;
- if (value < 0)
- return (tlv->lasterr = gpg_error (GPG_ERR_EOVERFLOW));
- }
- *r_value = value;
- tlv->offset += tlv->ti.length;
- return 0;
-}
-
-
-/* Variant of tlv_expect_integer which returns an MPI. If IGNORE_ZERO
- * is set a value of 0 is ignored and R_VALUE not changed and the
- * function returns GPG_ERR_FALSE. No check for negative encoded
- * integers is doe because the old code here worked the same and we
- * can't foreclose invalid encoded PKCS#12 stuff - after all it is
- * PKCS#12 see https://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html */
-static gpg_error_t
-tlv_expect_mpinteger (struct tlv_ctx_s *tlv, int ignore_zero,
- gcry_mpi_t *r_value)
-{
- const unsigned char *p;
- size_t n;
-
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER
- && !tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- p = tlv->buffer + tlv->offset;
- if (!(n=tlv->ti.length))
- return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
-
- tlv->offset += tlv->ti.length;
- if (ignore_zero && n == 1 && !*p)
- return gpg_error (GPG_ERR_FALSE);
-
- return gcry_mpi_scan (r_value, GCRYMPI_FMT_USG, p, n, NULL);
-}
-
-
-/* Expect that the current tag is an object id and store its value at
- * (R_OID,R_OIDLEN). Then skip over its value to the next tag. Note
- * that the stored value is not allocated but points into TLV. */
-static gpg_error_t
-tlv_expect_object_id (struct tlv_ctx_s *tlv,
- unsigned char const **r_oid, size_t *r_oidlen)
-{
- const unsigned char *p;
- size_t n;
-
- tlv->lastfunc = __func__;
- if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OBJECT_ID
- && !tlv->ti.is_constructed))
- return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ));
- p = tlv->buffer + tlv->offset;
- if (!(n=tlv->ti.length))
- return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT));
-
- *r_oid = p;
- *r_oidlen = tlv->ti.length;
- tlv->offset += tlv->ti.length;
- return 0;
-}
-
-
-
-/* Given an ASN.1 chunk of a structure like:
- *
- * 24 NDEF: OCTET STRING -- This is not passed to us
- * 04 1: OCTET STRING -- INPUT point s to here
- * : 30
- * 04 1: OCTET STRING
- * : 80
- * [...]
- * 04 2: OCTET STRING
- * : 00 00
- * : } -- This denotes a Null tag and are the last
- * -- two bytes in INPUT.
- *
- * The example is from Mozilla Firefox 1.0.4 which actually exports
- * certs as single byte chunks of octet strings.
- *
- * Create a new buffer with the content of that octet string. INPUT
- * is the original buffer with a LENGTH. Returns
- * NULL on error or a new malloced buffer with its actual used length
- * stored at R_NEWLENGTH. */
-static unsigned char *
-cram_octet_string (const unsigned char *input, size_t length,
- size_t *r_newlength)
-{
- const unsigned char *s = input;
- size_t n = length;
- unsigned char *output, *d;
- struct tag_info ti;
-
- /* Allocate output buf. We know that it won't be longer than the
- input buffer. */
- d = output = gcry_malloc (length);
- if (!output)
- goto bailout;
-
- while (n)
- {
- if (parse_tag (&s, &n, &ti))
- goto bailout;
- if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING
- && !ti.ndef && !ti.is_constructed)
- {
- memcpy (d, s, ti.length);
- s += ti.length;
- d += ti.length;
- n -= ti.length;
- }
- else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)
- break; /* Ready */
- else
- goto bailout;
- }
-
-
- *r_newlength = d - output;
- return output;
-
- bailout:
- gcry_free (output);
- return NULL;
-}
-
-
static int
string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw,
int req_keylen, unsigned char *keybuf)
@@ -988,13 +433,14 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
static int
set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
- const void *iv, size_t ivlen, const char *pw, int algo)
+ const void *iv, size_t ivlen, const char *pw,
+ int cipher_algo, int digest_algo)
{
unsigned char *keybuf;
size_t keylen;
int rc;
- keylen = gcry_cipher_get_algo_keylen (algo);
+ keylen = gcry_cipher_get_algo_keylen (cipher_algo);
if (!keylen)
return -1;
keybuf = gcry_malloc_secure (keylen);
@@ -1002,7 +448,7 @@ set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
return -1;
rc = gcry_kdf_derive (pw, strlen (pw),
- GCRY_KDF_PBKDF2, GCRY_MD_SHA1,
+ GCRY_KDF_PBKDF2, digest_algo,
salt, saltlen, iter, keylen, keybuf);
if (rc)
{
@@ -1033,7 +479,7 @@ set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter,
static void
crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
int iter, const void *iv, size_t ivlen,
- const char *pw, int cipher_algo, int encrypt)
+ const char *pw, int cipher_algo, int digest_algo, int encrypt)
{
gcry_cipher_hd_t chd;
int rc;
@@ -1047,7 +493,8 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
}
if ((cipher_algo == GCRY_CIPHER_AES128 || cipher_algo == GCRY_CIPHER_AES256)
- ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo)
+ ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw,
+ cipher_algo, digest_algo)
: set_key_iv (chd, salt, saltlen, iter, pw,
cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24))
{
@@ -1078,13 +525,13 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen,
and CIPHER_ALGO is the algorithm id to use. CHECK_FNC is a
function called with the plaintext and used to check whether the
decryption succeeded; i.e. that a correct passphrase has been
- given. That function shall return true if the decryption has likely
- succeeded. */
-static void
+ given. The function returns the length of the unpadded plaintext
+ or 0 on error. */
+static size_t
decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
char *salt, size_t saltlen,
int iter, const void *iv, size_t ivlen,
- const char *pw, int cipher_algo,
+ const char *pw, int cipher_algo, int digest_algo,
int (*check_fnc) (const void *, size_t))
{
static const char * const charsets[] = {
@@ -1109,6 +556,7 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
int charsetidx = 0;
char *convertedpw = NULL; /* Malloced and converted password or NULL. */
size_t convertedpwsize = 0; /* Allocated length. */
+ size_t plainlen = 0;
for (charsetidx=0; charsets[charsetidx]; charsetidx++)
{
@@ -1156,11 +604,33 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length,
}
memcpy (plaintext, ciphertext, length);
crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen,
- convertedpw? convertedpw:pw, cipher_algo, 0);
+ convertedpw? convertedpw:pw, cipher_algo, digest_algo, 0);
if (check_fnc (plaintext, length))
- break; /* Decryption succeeded. */
+ {
+ /* Strip the pkcs#7 padding. */
+ if (length)
+ {
+ int n, i;
+
+ n = plaintext[length-1];
+ if (n >= length || n > 16)
+ log_info ("decryption failed; invalid padding size\n");
+ else
+ {
+ for (i=1; i < n; i++)
+ if (plaintext[length-i-1] != n)
+ break;
+ if (i < n)
+ log_info ("decryption failed; invalid padding octet\n");
+ else
+ plainlen = length - n;
+ }
+ }
+ break; /* Decryption probably succeeded. */
+ }
}
gcry_free (convertedpw);
+ return plainlen;
}
@@ -1173,19 +643,21 @@ bag_decrypted_data_p (const void *plaintext, size_t length)
const unsigned char *p = plaintext;
size_t n = length;
- /* { */
- /* # warning debug code is enabled */
- /* FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); */
- /* if (!fp || fwrite (p, n, 1, fp) != 1) */
- /* exit (2); */
- /* fclose (fp); */
- /* } */
+#ifdef ENABLE_DER_STRUCT_DUMPING
+ {
+ # warning debug code is enabled
+ FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb");
+ if (!fp || fwrite (p, n, 1, fp) != 1)
+ exit (2);
+ fclose (fp);
+ }
+#endif /*ENABLE_DER_STRUCT_DUMPING*/
- if (parse_tag (&p, &n, &ti))
+ if (tlv_parse_tag (&p, &n, &ti))
return 0;
if (ti.class || ti.tag != TAG_SEQUENCE)
return 0;
- if (parse_tag (&p, &n, &ti))
+ if (tlv_parse_tag (&p, &n, &ti))
return 0;
return 1;
@@ -1193,7 +665,7 @@ bag_decrypted_data_p (const void *plaintext, size_t length)
static int
-parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
+parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv)
{
gpg_error_t err = 0;
const char *where;
@@ -1213,7 +685,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
int keyelem_count;
int renewed_tlv = 0;
int loopcount;
- unsigned int startlevel;
+ unsigned int startlevel, startlevel2;
+ int digest_algo = GCRY_MD_SHA1;
where = "bag.encryptedData";
if (opt_verbose)
@@ -1283,6 +756,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
/*FIXME: This code is duplicated in parse_shrouded_key_bag. */
if (is_pbes2)
{
+ size_t parmlen; /* Remaining length of the parameter sequence. */
+
where = "pkcs5PBES2-params";
if (tlv_next (tlv))
goto bailout;
@@ -1309,11 +784,13 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
goto bailout;
if (tlv_expect_sequence (tlv))
goto bailout;
+ parmlen = tlv_parser_tag_length (tlv, 0);
if (tlv_next (tlv))
goto bailout;
if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
+ parmlen -= tlv_parser_tag_length (tlv, 1);
if (datalen < 8 || datalen > sizeof salt)
{
log_info ("bad length of salt (%zu)\n", datalen);
@@ -1327,6 +804,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
goto bailout;
if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
+ parmlen -= tlv_parser_tag_length (tlv, 1);
if (!intval) /* Not a valid iteration count. */
{
err = gpg_error (GPG_ERR_INV_VALUE);
@@ -1334,8 +812,36 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
}
iter = intval;
- /* Note: We don't support the optional parameters but assume
- that the algorithmIdentifier follows. */
+ if (parmlen > 2) /* There is the optional prf. */
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_sequence (tlv))
+ goto bailout;
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+ digest_algo = digest_algo_from_oid (oid, oidlen);
+ if (!digest_algo)
+ {
+ gpgrt_log_printhex (oid, oidlen, "kdf digest algo:");
+ err = gpg_error (GPG_ERR_DIGEST_ALGO);
+ goto bailout;
+ }
+ if (opt_verbose > 1)
+ log_debug ("kdf digest algo = %d\n", digest_algo);
+
+ if (tlv_peek_null (tlv))
+ {
+ /* Read the optional Null tag. */
+ if (tlv_next (tlv))
+ goto bailout;
+ }
+ }
+ else
+ digest_algo = GCRY_MD_SHA1;
+
if (tlv_next (tlv))
goto bailout;
if (tlv_expect_sequence (tlv))
@@ -1421,15 +927,21 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
log_error ("error allocating decryption buffer\n");
goto bailout;
}
- decrypt_block (data, plain, datalen, salt, saltlen, iter,
+ datalen = decrypt_block (data, plain, datalen, salt, saltlen, iter,
iv, is_pbes2?16:0, ctx->password,
is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) :
is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40,
+ digest_algo,
bag_decrypted_data_p);
+ if (!datalen)
+ {
+ err = gpg_error (GPG_ERR_DECRYPT_FAILED);
+ goto bailout;
+ }
/* We do not need the TLV anymore and allocated a new one. */
where = "bag.encryptedData.decrypted-text";
- tlv = tlv_new (plain, datalen);
+ tlv = tlv_parser_new (plain, datalen, opt_verbose);
if (!tlv)
{
err = gpg_error_from_syserror ();
@@ -1442,7 +954,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
ctx->badpass = 1;
goto bailout;
}
- if (tlv_expect_top_sequence (tlv))
+ if (tlv_expect_sequence (tlv))
{
ctx->badpass = 1;
goto bailout;
@@ -1450,8 +962,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
/* Loop over all certificates inside the bag. */
loopcount = 0;
- startlevel = tlv_level (tlv);
- while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel)
+ startlevel = tlv_parser_level (tlv);
+ while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel)
{
int iscrlbag = 0;
int iskeybag = 0;
@@ -1564,7 +1076,8 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
where = "reading.keybag.key-parameters";
keyelem_count = 0;
- while (!(err = tlv_next (tlv)) && !tlv_popped (tlv))
+ startlevel2 = tlv_parser_level (tlv);
+ while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel2)
{
if (keyelem_count >= 9)
{
@@ -1586,7 +1099,9 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
log_debug ("RSA key parameter %d found\n", keyelem_count);
keyelem_count++;
}
- if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ if (!err)
+ tlv_parser_set_pending (tlv);
+ else if (err && gpg_err_code (err) != GPG_ERR_EOF)
goto bailout;
err = 0;
}
@@ -1635,24 +1150,21 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
/* Skip the optional SET with the pkcs12 cert attributes. */
where = "bag.attribute_set";
- err = tlv_next (tlv);
- if (gpg_err_code (err) == GPG_ERR_EOF)
- break;
- if (err)
- goto bailout;
- err = tlv_expect_set (tlv);
- if (!err)
- { /* This is the optional set of attributes. Skip it. */
- tlv_skip (tlv);
+ if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET))
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ err = tlv_expect_set (tlv);
+ if (err)
+ goto bailout;
+ tlv_parser_skip (tlv);
if (opt_verbose)
- log_info ("skipping bag.attribute_set\n");
+ log_info ("skipping %s\n", where);
}
- else if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
- tlv_set_pending (tlv); /* The next tlv_next will be skipped. */
- else
- goto bailout;
}
- if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ if (!err)
+ tlv_parser_set_pending (tlv);
+ else if (err && gpg_err_code (err) != GPG_ERR_EOF)
{
if (!loopcount) /* The first while(tlv_next) failed. */
ctx->badpass = 1;
@@ -1662,7 +1174,7 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
leave:
if (renewed_tlv)
- tlv_release (tlv);
+ tlv_parser_release (tlv);
gcry_free (plain);
if (ctx->badpass)
{
@@ -1676,12 +1188,11 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
bailout:
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
- log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ log_error ("%s(%s): lvl=%u (%s): %s - %s\n",
__func__, where,
- tlv? tlv->stacklen : 0,
- tlv? tlv->offset : 0,
- tlv? tlv->lastfunc : "",
- tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ tlv_parser_level (tlv),
+ tlv_parser_lastfunc (tlv),
+ tlv_parser_lasterrstr (tlv),
gpg_strerror (err));
goto leave;
}
@@ -1696,17 +1207,19 @@ bag_data_p (const void *plaintext, size_t length)
const unsigned char *p = plaintext;
size_t n = length;
-/* { */
-/* # warning debug code is enabled */
-/* FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); */
-/* if (!fp || fwrite (p, n, 1, fp) != 1) */
-/* exit (2); */
-/* fclose (fp); */
-/* } */
+#ifdef ENABLE_DER_STRUCT_DUMPING
+ {
+# warning debug code is enabled
+ FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb");
+ if (!fp || fwrite (p, n, 1, fp) != 1)
+ exit (2);
+ fclose (fp);
+ }
+#endif /*ENABLE_DER_STRUCT_DUMPING*/
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
+ if (tlv_parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE)
return 0;
- if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
+ if (tlv_parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER
|| ti.length != 1 || *p)
return 0;
@@ -1715,7 +1228,7 @@ bag_data_p (const void *plaintext, size_t length)
static gpg_error_t
-parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
+parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv)
{
gpg_error_t err = 0;
const char *where;
@@ -1728,11 +1241,12 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
size_t saltlen;
char iv[16];
unsigned int iter;
- struct tlv_ctx_s *saved_tlv = NULL;
+ tlv_parser_t saved_tlv = NULL;
int renewed_tlv = 0; /* True if the TLV must be released. */
unsigned char *plain = NULL;
int is_pbes2 = 0;
int is_aes256 = 0;
+ int digest_algo = GCRY_MD_SHA1;
where = "shrouded_key_bag";
if (opt_verbose)
@@ -1774,6 +1288,8 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
if (is_pbes2)
{
+ size_t parmlen; /* Remaining length of the parameter sequence. */
+
where = "shrouded_key_bag.pkcs5PBES2-params";
if (tlv_next (tlv))
goto bailout;
@@ -1797,11 +1313,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
goto bailout;
if (tlv_expect_sequence (tlv))
goto bailout;
+ parmlen = tlv_parser_tag_length (tlv, 0);
if (tlv_next (tlv))
goto bailout;
if (tlv_expect_octet_string (tlv, 0, &data, &datalen))
goto bailout;
+ parmlen -= tlv_parser_tag_length (tlv, 1);
if (datalen < 8 || datalen > sizeof salt)
{
log_info ("bad length of salt (%zu) for AES\n", datalen);
@@ -1815,6 +1333,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
goto bailout;
if ((err = tlv_expect_integer (tlv, &intval)))
goto bailout;
+ parmlen -= tlv_parser_tag_length (tlv, 1);
if (!intval) /* Not a valid iteration count. */
{
err = gpg_error (GPG_ERR_INV_VALUE);
@@ -1822,8 +1341,36 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
}
iter = intval;
- /* Note: We don't support the optional parameters but assume
- that the algorithmIdentifier follows. */
+ if (parmlen > 2) /* There is the optional prf. */
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_sequence (tlv))
+ goto bailout;
+ if (tlv_next (tlv))
+ goto bailout;
+ if (tlv_expect_object_id (tlv, &oid, &oidlen))
+ goto bailout;
+ digest_algo = digest_algo_from_oid (oid, oidlen);
+ if (!digest_algo)
+ {
+ gpgrt_log_printhex (oid, oidlen, "kdf digest algo:");
+ err = gpg_error (GPG_ERR_DIGEST_ALGO);
+ goto bailout;
+ }
+ if (opt_verbose > 1)
+ log_debug ("kdf digest algo = %d\n", digest_algo);
+
+ if (tlv_peek_null (tlv))
+ {
+ /* Read the optional Null tag. */
+ if (tlv_next (tlv))
+ goto bailout;
+ }
+ }
+ else
+ digest_algo = GCRY_MD_SHA1;
+
if (tlv_next (tlv))
goto bailout;
if (tlv_expect_sequence (tlv))
@@ -1905,17 +1452,22 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
log_error ("error allocating decryption buffer\n");
goto bailout;
}
- decrypt_block (data, plain, datalen, salt, saltlen, iter,
+ datalen = decrypt_block (data, plain, datalen, salt, saltlen, iter,
iv, is_pbes2? 16:0, ctx->password,
is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128)
: GCRY_CIPHER_3DES,
+ digest_algo,
bag_data_p);
-
+ if (!datalen)
+ {
+ err = gpg_error (GPG_ERR_DECRYPT_FAILED);
+ goto bailout;
+ }
/* We do not need the TLV anymore and allocated a new one. */
where = "shrouded_key_bag.decrypted-text";
saved_tlv = tlv;
- tlv = tlv_new (plain, datalen);
+ tlv = tlv_parser_new (plain, datalen, opt_verbose);
if (!tlv)
{
err = gpg_error_from_syserror ();
@@ -1930,7 +1482,7 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
ctx->badpass = 1;
goto bailout;
}
- if (tlv_expect_top_sequence (tlv))
+ if (tlv_expect_sequence (tlv))
{
ctx->badpass = 1;
goto bailout;
@@ -1967,10 +1519,13 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
{
if (opt_verbose > 1)
log_debug ("RSA parameters\n");
- if (tlv_next (tlv))
- goto bailout;
- if (tlv_expect_null (tlv))
- tlv_set_pending (tlv); /* NULL tag missing - ignore this. */
+
+ if (tlv_peek_null (tlv))
+ {
+ /* Read the optional Null tag. */
+ if (tlv_next (tlv))
+ goto bailout;
+ }
}
else if (oidlen == DIM(oid_pcPublicKey)
&& !memcmp (oid, oid_pcPublicKey, oidlen))
@@ -2055,8 +1610,9 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
{
int keyelem_count = 0;
int firstparam = 1;
+ unsigned int startlevel = tlv_parser_level (tlv);
- while (!(err = tlv_next (tlv)) && !tlv_popped (tlv))
+ while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel)
{
if (keyelem_count >= 9)
{
@@ -2082,41 +1638,39 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
}
firstparam = 0;
}
- if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ if (!err)
+ tlv_parser_set_pending (tlv);
+ else if (err && gpg_err_code (err) != GPG_ERR_EOF)
goto bailout;
err = 0;
}
if (opt_verbose > 1)
log_debug ("restoring parser context\n");
- tlv_release (tlv);
+ tlv_parser_release (tlv);
renewed_tlv = 0;
tlv = saved_tlv;
where = "shrouded_key_bag.attribute_set";
- err = tlv_next (tlv);
- if (gpg_err_code (err) == GPG_ERR_EOF)
- goto leave;
- if (err)
- goto bailout;
- err = tlv_expect_set (tlv);
- if (!err)
- { /* This is the optional set of attributes. Skip it. */
- tlv_skip (tlv);
+ /* Check for an optional set of attributes. */
+ if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET))
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ err = tlv_expect_set (tlv);
+ if (err)
+ goto bailout;
+ tlv_parser_skip (tlv);
if (opt_verbose)
log_info ("skipping %s\n", where);
}
- else if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
- tlv_set_pending (tlv); /* The next tlv_next will be skipped. */
- else /* Other error. */
- goto bailout;
leave:
gcry_free (plain);
if (renewed_tlv)
{
- tlv_release (tlv);
+ tlv_parser_release (tlv);
if (opt_verbose > 1)
log_debug ("parser context released\n");
}
@@ -2125,19 +1679,18 @@ parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
bailout:
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
- log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ log_error ("%s(%s): lvl=%u (%s): %s - %s\n",
__func__, where,
- tlv? tlv->stacklen : 0,
- tlv? tlv->offset : 0,
- tlv? tlv->lastfunc : "",
- tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ tlv_parser_level (tlv),
+ tlv_parser_lastfunc (tlv),
+ tlv_parser_lasterrstr (tlv),
gpg_strerror (err));
goto leave;
}
static gpg_error_t
-parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
+parse_cert_bag (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv)
{
gpg_error_t err = 0;
const char *where;
@@ -2204,30 +1757,29 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
* SEQUENCE -- we actually ignore this.
*/
where = "certbag.attribute_set";
- if (tlv_next (tlv))
- goto bailout;
- err = tlv_expect_set (tlv);
- if (!err)
- { /* This is the optional set of attributes. Skip it. */
- tlv_skip (tlv);
+ /* Check for an optional set of attributes. */
+ if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET))
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ err = tlv_expect_set (tlv);
+ if (err)
+ goto bailout;
+ tlv_parser_skip (tlv);
if (opt_verbose)
- log_info ("skipping certbag.attribute_set\n");
+ log_info ("skipping %s\n", where);
}
- else if (gpg_err_code (err) == GPG_ERR_INV_OBJ)
- tlv_set_pending (tlv); /* The next tlv_next will be skipped. */
- else
- goto bailout;
+
leave:
return err;
bailout:
- log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ log_error ("%s(%s): lvl=%u (%s): %s - %s\n",
__func__, where,
- tlv? tlv->stacklen : 0,
- tlv? tlv->offset : 0,
- tlv? tlv->lastfunc : "",
- tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ tlv_parser_level (tlv),
+ tlv_parser_lastfunc (tlv),
+ tlv_parser_lasterrstr (tlv),
gpg_strerror (err));
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
@@ -2236,7 +1788,7 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
static gpg_error_t
-parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
+parse_bag_data (struct p12_parse_ctx_s *ctx, tlv_parser_t tlv)
{
gpg_error_t err = 0;
const char *where;
@@ -2263,6 +1815,14 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
if (tlv_expect_octet_string (tlv, 1, NULL, NULL))
goto bailout;
+ if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING))
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ err = tlv_expect_octet_string (tlv, 1, NULL, NULL);
+ if (err)
+ goto bailout;
+ }
/* Expect:
* SEQUENCE
@@ -2273,8 +1833,9 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
if (tlv_expect_sequence (tlv))
goto bailout;
- startlevel = tlv_level (tlv);
- while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel)
+ startlevel = tlv_parser_level (tlv);
+ tlv_parser_dump_state ("data.outerseqs", "beginloop", tlv);
+ while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel)
{
/* Expect:
* SEQUENCE
@@ -2308,15 +1869,16 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
}
else
{
- tlv_skip (tlv);
+ tlv_parser_skip (tlv);
log_info ("unknown inner data type - skipped\n");
}
}
- if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ tlv_parser_dump_state ("data.outerseqs", "endloop", tlv);
+ if (!err)
+ tlv_parser_set_pending (tlv);
+ else if (err && gpg_err_code (err) != GPG_ERR_EOF)
goto bailout;
err = 0;
- if (tlv_popped (tlv))
- tlv_set_pending (tlv);
leave:
return err;
@@ -2324,12 +1886,11 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv)
bailout:
if (!err)
err = gpg_error (GPG_ERR_GENERAL);
- log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ log_error ("%s(%s): lvl=%u (%s): %s - %s\n",
__func__, where,
- tlv? tlv->stacklen : 0,
- tlv? tlv->offset : 0,
- tlv? tlv->lastfunc : "",
- tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ tlv_parser_level (tlv),
+ tlv_parser_lastfunc (tlv),
+ tlv_parser_lasterrstr (tlv),
gpg_strerror (err));
goto leave;
}
@@ -2349,7 +1910,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
{
gpg_error_t err;
const char *where = "";
- struct tlv_ctx_s *tlv;
+ tlv_parser_t tlv;
struct p12_parse_ctx_s ctx = { NULL };
const unsigned char *oid;
size_t oidlen;
@@ -2362,7 +1923,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
ctx.certcbarg = certcbarg;
ctx.password = pw;
- tlv = tlv_new (buffer, length);
+ tlv = tlv_parser_new (buffer, length, opt_verbose);
if (!tlv)
{
err = gpg_error_from_syserror ();
@@ -2404,6 +1965,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
if (tlv_expect_octet_string (tlv, 1, NULL, NULL))
goto bailout;
+ if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING))
+ {
+ if (tlv_next (tlv))
+ goto bailout;
+ err = tlv_expect_octet_string (tlv, 1, NULL, NULL);
+ if (err)
+ goto bailout;
+ }
where = "bags";
if (tlv_next (tlv))
@@ -2411,10 +1980,12 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
if (tlv_expect_sequence (tlv))
goto bailout;
- startlevel = tlv_level (tlv);
- while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel)
+ startlevel = tlv_parser_level (tlv);
+ tlv_parser_dump_state ("bags", "beginloop", tlv);
+ while (!(err = tlv_next (tlv)) && tlv_parser_level (tlv) == startlevel)
{
where = "bag-sequence";
+ tlv_parser_dump_state (where, NULL, tlv);
if (tlv_expect_sequence (tlv))
goto bailout;
@@ -2447,15 +2018,18 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
}
else
{
- tlv_skip (tlv);
+ tlv_parser_skip (tlv);
log_info ("unknown outer bag type - skipped\n");
}
}
- if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ tlv_parser_dump_state ("bags", "endloop", tlv);
+ if (!err)
+ tlv_parser_set_pending (tlv);
+ else if (err && gpg_err_code (err) != GPG_ERR_EOF)
goto bailout;
err = 0;
- tlv_release (tlv);
+ tlv_parser_release (tlv);
if (r_curve)
*r_curve = ctx.curve;
else
@@ -2465,12 +2039,12 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
bailout:
*r_badpass = ctx.badpass;
- log_error ("%s(%s): offset %u.%zu (%s): %s - %s\n",
+ log_error ("%s(%s): @%04zu lvl=%u %s: %s - %s\n",
__func__, where,
- tlv? tlv->stacklen : 0,
- tlv? tlv->offset : 0,
- tlv? tlv->lastfunc : "",
- tlv ? gpg_strerror (tlv->lasterr) : "init failed",
+ tlv_parser_offset (tlv),
+ tlv_parser_level (tlv),
+ tlv_parser_lastfunc (tlv),
+ tlv_parser_lasterrstr (tlv),
gpg_strerror (err));
if (ctx.privatekey)
{
@@ -2481,7 +2055,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw,
gcry_free (ctx.privatekey);
ctx.privatekey = NULL;
}
- tlv_release (tlv);
+ tlv_parser_release (tlv);
gcry_free (ctx.curve);
if (r_curve)
*r_curve = NULL;
@@ -3423,7 +2997,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
/* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw,
- GCRY_CIPHER_RFC2268_40, 1);
+ GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 1);
/* Encode the encrypted stuff into a bag. */
seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n);
@@ -3455,7 +3029,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen,
/* Encrypt it. */
gcry_randomize (salt, 8, GCRY_STRONG_RANDOM);
crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0,
- pw, GCRY_CIPHER_3DES, 1);
+ pw, GCRY_CIPHER_3DES, GCRY_MD_SHA1, 1);
/* Encode the encrypted stuff into a bag. */
if (cert && certlen)
diff --git a/sm/server.c b/sm/server.c
index 3ebf47299..f00b70d38 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -287,6 +287,17 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
ctrl->offline = i;
}
}
+ else if (!strcmp (key, "always-trust"))
+ {
+ /* We ignore this option if gpgsm has been started with
+ --always-trust (which also sets offline) and if
+ --require-compliance is active */
+ if (!opt.always_trust && !opt.require_compliance)
+ {
+ int i = *value? !!atoi (value) : 1;
+ ctrl->always_trust = i;
+ }
+ }
else if (!strcmp (key, "request-origin"))
{
if (!opt.request_origin)
@@ -320,6 +331,7 @@ reset_notify (assuan_context_t ctx, char *line)
gpgsm_release_certlist (ctrl->server_local->signerlist);
ctrl->server_local->recplist = NULL;
ctrl->server_local->signerlist = NULL;
+ ctrl->always_trust = 0;
close_message_fp (ctrl);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
@@ -495,6 +507,7 @@ cmd_encrypt (assuan_context_t ctx, char *line)
gpgsm_release_certlist (ctrl->server_local->recplist);
ctrl->server_local->recplist = NULL;
+ ctrl->always_trust = 0;
/* Close and reset the fp and the fds */
close_message_fp (ctrl);
assuan_close_input_fd (ctx);
@@ -1221,7 +1234,8 @@ static const char hlp_getinfo[] =
" agent-check - Return success if the agent is running.\n"
" cmd_has_option CMD OPT\n"
" - Returns OK if the command CMD implements the option OPT.\n"
- " offline - Returns OK if the connection is in offline mode.";
+ " offline - Returns OK if the connection is in offline mode."
+ " always-trust- Returns OK if the connection is in always-trust mode.";
static gpg_error_t
cmd_getinfo (assuan_context_t ctx, char *line)
{
@@ -1280,6 +1294,11 @@ cmd_getinfo (assuan_context_t ctx, char *line)
{
rc = ctrl->offline? 0 : gpg_error (GPG_ERR_FALSE);
}
+ else if (!strcmp (line, "always-trust"))
+ {
+ rc = (ctrl->always_trust || opt.always_trust)? 0
+ /**/ : gpg_error (GPG_ERR_FALSE);
+ }
else
rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
diff --git a/sm/sign.c b/sm/sign.c
index 39ff2b58f..8fd111a7d 100644
--- a/sm/sign.c
+++ b/sm/sign.c
@@ -622,6 +622,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
certlist_t cl;
int release_signerlist = 0;
int binary_detached = detached && !ctrl->create_pem && !ctrl->create_base64;
+ char *curve = NULL;
audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN);
@@ -760,7 +761,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
unsigned int nbits;
int pk_algo;
- pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits);
+ xfree (curve);
+ pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits, &curve);
cl->pk_algo = pk_algo;
if (opt.forced_digest_algo)
@@ -820,8 +822,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
goto leave;
}
- if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0,
- NULL, nbits, NULL))
+ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0,
+ NULL, nbits, curve))
{
char kidstr[10+1];
@@ -1187,6 +1189,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist,
gpg_strerror (rc), gpg_strsource (rc) );
if (release_signerlist)
gpgsm_release_certlist (signerlist);
+ xfree (curve);
ksba_cms_release (cms);
gnupg_ksba_destroy_writer (b64writer);
keydb_release (kh);
diff --git a/sm/t-minip12.c b/sm/t-minip12.c
index de6b7e5cc..bf3177ea0 100644
--- a/sm/t-minip12.c
+++ b/sm/t-minip12.c
@@ -559,13 +559,21 @@ run_one_test (const char *name, const char *desc, const char *pass,
else if (!certexpected && certstr)
printresult ("FAIL: %s - no certs expected but got one\n", name);
else if (certexpected && certstr && strcmp (certexpected, certstr))
- printresult ("FAIL: %s - certs not as expected\n", name);
+ {
+ printresult ("FAIL: %s - certs not as expected\n", name);
+ inf ("cert(exp)=%s", certexpected);
+ inf ("cert(got)=%s", certstr? certstr:"[null]");
+ }
else if (keyexpected && !resulthash)
printresult ("FAIL: %s - expected key but got none\n", name);
else if (!keyexpected && resulthash)
printresult ("FAIL: %s - key not expected but got one\n", name);
else if (keyexpected && resulthash && strcmp (keyexpected, resulthash))
- printresult ("FAIL: %s - keys not as expected\n", name);
+ {
+ printresult ("FAIL: %s - keys not as expected\n", name);
+ inf ("key(exp)=%s", keyexpected);
+ inf ("key(got)=%s", resulthash? resulthash:"[null]");
+ }
else
{
printresult ("PASS: %s\n", name);
diff --git a/sm/verify.c b/sm/verify.c
index de407bf16..9c012596d 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -450,7 +450,7 @@ gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp,
pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
pkalgostr = gpgsm_pubkey_algo_string (cert, NULL);
- pkalgo = gpgsm_get_key_algo_info2 (cert, &nbits, &pkcurve);
+ pkalgo = gpgsm_get_key_algo_info (cert, &nbits, &pkcurve);
/* Remap the ECC algo to the algo we use. Note that EdDSA has
* already been mapped. */
if (pkalgo == GCRY_PK_ECC)
@@ -486,7 +486,7 @@ gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp,
/* Check compliance. */
if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION,
- pkalgo, pkalgoflags, NULL, nbits, NULL))
+ pkalgo, pkalgoflags, NULL, nbits, pkcurve))
{
char kidstr[10+1];
diff --git a/tests/cms/Makefile.am b/tests/cms/Makefile.am
index 7efdf37b1..b43fb1c91 100644
--- a/tests/cms/Makefile.am
+++ b/tests/cms/Makefile.am
@@ -99,6 +99,8 @@ EXTRA_DIST = $(XTESTS) $(KEYS) $(CERTS) $(TEST_FILES) \
samplekeys/opensc-test.p12 \
samplekeys/t5793-openssl.pfx \
samplekeys/t5793-test.pfx \
+ samplekeys/edward.tester@demo.gnupg.com.p12 \
+ samplekeys/nistp256-openssl-self-signed.p12 \
samplemsgs/pwri-sample.cbc.p7m \
samplemsgs/pwri-sample.cbc-2.p7m \
samplemsgs/pwri-sample.gcm.p7m \
diff --git a/tests/cms/samplekeys/Description-p12 b/tests/cms/samplekeys/Description-p12
index f882de9ea..01276087f 100644
--- a/tests/cms/samplekeys/Description-p12
+++ b/tests/cms/samplekeys/Description-p12
@@ -1,4 +1,6 @@
# Description-p12 - Machine readable description of our P12 test vectors
+# The Cert line gives the SHA1 fingerprint of the certificate
+# The Key line gives a hash of the key parameters as returned by minip12.c
Name: ov-user.p12
Desc: Private test key from www.openvalidation.org
@@ -30,3 +32,23 @@ Desc: QuaVadis format of t5793-openssl
Pass: test
Cert: 80348a438e4b803b99e708da0b7fdd0659dedd15
Key: c271e44ab4fb19ca1aae71102ea4d7292ccc981d
+
+Name: edward.tester@demo.gnupg.com.p12
+Desc: GnuPG exported Brainpool certificate
+Pass: abc,123456
+Cert: ff810b9281a43c394aa138e9c7fd4c0193216fa6
+Key: 94c6d0b067370a8f2a09ae43cfe8d700bbd61e75
+
+Name: nistp256-openssl-self-signed.p12
+Desc: OpenSSL generated self-signed nistp256 key+cert
+Pass: abc
+Cert: 5cea0c5bf09ccd92535267c662fc098f6c81c27e
+Key: 3cb2fba95d1976df69eb7aa8c65ac5354e15af32
+
+Name: t6752-ov-user-ff.p12
+Desc: Mozilla generated with a surplus octet string container
+Pass: start
+Cert: 4753a910e0c8b4caa8663ca0e4273a884eb5397d
+Key: 93be89edd11214ab74280d988a665b6beef876c5
+
+# eof #
diff --git a/tests/cms/samplekeys/edward.tester@demo.gnupg.com.p12 b/tests/cms/samplekeys/edward.tester@demo.gnupg.com.p12
new file mode 100644
index 000000000..a6f983780
Binary files /dev/null and b/tests/cms/samplekeys/edward.tester@demo.gnupg.com.p12 differ
diff --git a/tests/cms/samplekeys/nistp256-openssl-self-signed.p12 b/tests/cms/samplekeys/nistp256-openssl-self-signed.p12
new file mode 100644
index 000000000..9eeebdae3
Binary files /dev/null and b/tests/cms/samplekeys/nistp256-openssl-self-signed.p12 differ
diff --git a/tests/cms/samplekeys/t6752-ov-user-ff.p12 b/tests/cms/samplekeys/t6752-ov-user-ff.p12
new file mode 100644
index 000000000..153ffb000
Binary files /dev/null and b/tests/cms/samplekeys/t6752-ov-user-ff.p12 differ
diff --git a/tools/card-call-scd.c b/tools/card-call-scd.c
index 08f951331..2bdbacb7a 100644
--- a/tools/card-call-scd.c
+++ b/tools/card-call-scd.c
@@ -235,6 +235,7 @@ status_sc_op_failure (gpg_error_t err)
gnupg_status_printf (STATUS_SC_OP_FAILURE, "1");
break;
case GPG_ERR_BAD_PIN:
+ case GPG_ERR_BAD_RESET_CODE:
gnupg_status_printf (STATUS_SC_OP_FAILURE, "2");
break;
default:
diff --git a/tools/card-yubikey.c b/tools/card-yubikey.c
index ece7edc48..63d49c762 100644
--- a/tools/card-yubikey.c
+++ b/tools/card-yubikey.c
@@ -332,6 +332,8 @@ yubikey_commands (card_info_t info, estream_t fp, int argc, const char *argv[])
cmd = ykDISABLE;
else
{
+ log_info ("Please use \"%s\" to list the available sub-commands\n",
+ "help yubikey");
err = gpg_error (GPG_ERR_UNKNOWN_COMMAND);
goto leave;
}