aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2023-11-07 19:07:45 +0000
committerWerner Koch <[email protected]>2023-11-07 19:38:27 +0000
commit387ee7dcbd77d19687af967901ed4818cbdb8b3c (patch)
tree3fc63cd303b2d07454794064e4e6528df2dbf42c /common
parentdoc: Use the em dash to mark a break in a sentence. (diff)
parentw32: Use utf8 for the asctimestamp function. (diff)
downloadgnupg-387ee7dcbd77d19687af967901ed4818cbdb8b3c.tar.gz
gnupg-387ee7dcbd77d19687af967901ed4818cbdb8b3c.zip
Merge branch 'STABLE-BRANCH-2-4'
* common/b64dec.c (b64decode): Move to ... * common/miscellaneous.c: here. * common/t-b64.c: Re-inroduce and keep only the b64decode test code.
Diffstat (limited to 'common')
-rw-r--r--common/Makefile.am6
-rw-r--r--common/b64dec.c254
-rw-r--r--common/b64enc.c422
-rw-r--r--common/dotlock.c94
-rw-r--r--common/gettime.c264
-rw-r--r--common/gettime.h9
-rw-r--r--common/mbox-util.c31
-rw-r--r--common/miscellaneous.c50
-rw-r--r--common/mischelp.c77
-rw-r--r--common/mischelp.h6
-rw-r--r--common/recsel.c33
-rw-r--r--common/stringhelp.c31
-rw-r--r--common/stringhelp.h1
-rw-r--r--common/sysutils.c7
-rw-r--r--common/t-b64.c221
-rw-r--r--common/t-gettime.c52
-rw-r--r--common/tlv-parser.c788
-rw-r--r--common/tlv.h67
-rw-r--r--common/util.h10
19 files changed, 1360 insertions, 1063 deletions
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 <https://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#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 <https://www.gnu.org/licenses/>.
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#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 <stdio.h>
- #include <stdint.h>
-
- #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 (timeout > 0)
- {
- if (wtime > timeout)
- wtime = timeout;
- timeout -= wtime;
- }
+ if (ownerchanged)
+ wtime = 0; /* Reset because owner chnaged. */
- sumtime += wtime;
+ wtimereal = next_wait_interval (&wtime, &timeout);
+
+ 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;
-
- if (timeout > 0)
- {
- if (wtime > timeout)
- wtime = timeout;
- timeout -= wtime;
- }
+ int wtimereal;
+
+ 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 <langinfo.h>
#endif
+#ifdef HAVE_W32_SYSTEM
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif /*!HAVE_W32_SYSTEM*/
+#include <stdint.h> /* 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 <time.h> /* We need time_t. */
#include <gpg-error.h> /* We need gpg_error_t. */
-
+#include <stdint.h> /* 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.h>
-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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
*/
-/*
-
- As of now this is only a test program for manual tests.
-
- */
-
-
-
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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)
-{
- gpg_error_t err;
- struct b64state state;
-
- 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 ();
-}
-
-
-static void
-test_b64enc_file (const char *fname)
+/* 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;
- FILE *fp;
- char buffer[50];
- size_t nread;
+ const char *s;
+ unsigned char *buffer;
+ size_t length;
- fp = fname ? fopen (fname, "r") : stdin;
- if (!fp)
+ buffer = xmalloc (strlen(string)/2+1);
+ length = 0;
+ for (s=string; *s; s +=2 )
{
- fprintf (stderr, "%s:%d: can't open '%s': %s\n",
- __FILE__, __LINE__, fname? fname:"[stdin]", strerror (errno));
- fail (0);
+ if (!hexdigitp (s) || !hexdigitp (s+1))
+ return NULL; /* Invalid hex digits. */
+ ((unsigned char*)buffer)[length++] = xtoi_2 (s);
}
-
- 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 ();
+ *r_length = length;
+ return buffer;
}
+
static void
-test_b64dec_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, nbytes;
+ 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 = 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 ();
+ xfree (wantdata);
+ xfree (data);
}
+
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
@@ -44,6 +44,56 @@ static int errcount;
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)
{
struct { const char *string; time_t expected; } array [] = {
@@ -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 <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gpg-error.h>
+
+#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. */