diff options
author | Werner Koch <[email protected]> | 2023-11-07 19:07:45 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2023-11-07 19:38:27 +0000 |
commit | 387ee7dcbd77d19687af967901ed4818cbdb8b3c (patch) | |
tree | 3fc63cd303b2d07454794064e4e6528df2dbf42c /common | |
parent | doc: Use the em dash to mark a break in a sentence. (diff) | |
parent | w32: Use utf8 for the asctimestamp function. (diff) | |
download | gnupg-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.am | 6 | ||||
-rw-r--r-- | common/b64dec.c | 254 | ||||
-rw-r--r-- | common/b64enc.c | 422 | ||||
-rw-r--r-- | common/dotlock.c | 94 | ||||
-rw-r--r-- | common/gettime.c | 264 | ||||
-rw-r--r-- | common/gettime.h | 9 | ||||
-rw-r--r-- | common/mbox-util.c | 31 | ||||
-rw-r--r-- | common/miscellaneous.c | 50 | ||||
-rw-r--r-- | common/mischelp.c | 77 | ||||
-rw-r--r-- | common/mischelp.h | 6 | ||||
-rw-r--r-- | common/recsel.c | 33 | ||||
-rw-r--r-- | common/stringhelp.c | 31 | ||||
-rw-r--r-- | common/stringhelp.h | 1 | ||||
-rw-r--r-- | common/sysutils.c | 7 | ||||
-rw-r--r-- | common/t-b64.c | 221 | ||||
-rw-r--r-- | common/t-gettime.c | 52 | ||||
-rw-r--r-- | common/tlv-parser.c | 788 | ||||
-rw-r--r-- | common/tlv.h | 67 | ||||
-rw-r--r-- | common/util.h | 10 |
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. */ |