diff options
Diffstat (limited to 'common')
40 files changed, 1423 insertions, 519 deletions
diff --git a/common/Makefile.am b/common/Makefile.am index bd281d6..65ce35b 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -168,16 +168,15 @@ 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-name-value t-ccparray t-recsel t-w32-cmdline -if !HAVE_W32CE_SYSTEM -module_tests += t-exechelp t-exectool -endif + t-name-value t-ccparray t-recsel t-w32-cmdline t-b64 if HAVE_W32_SYSTEM module_tests += t-w32-reg +else +module_tests += t-exechelp t-exectool endif if MAINTAINER_MODE -module_maint_tests = t-helpfile t-b64 +module_maint_tests = t-helpfile else module_maint_tests = endif diff --git a/common/Makefile.in b/common/Makefile.in index d78f9c0..37b92d8 100644 --- a/common/Makefile.in +++ b/common/Makefile.in @@ -152,24 +152,23 @@ noinst_PROGRAMS = $(am__EXEEXT_3) $(am__EXEEXT_4) @HAVE_W32CE_SYSTEM_TRUE@@HAVE_W32_SYSTEM_TRUE@am__append_10 = exechelp-w32ce.c @HAVE_W32CE_SYSTEM_FALSE@@HAVE_W32_SYSTEM_TRUE@am__append_11 = exechelp-w32.c @HAVE_W32_SYSTEM_FALSE@am__append_12 = exechelp-posix.c -@HAVE_W32CE_SYSTEM_FALSE@am__append_13 = t-exechelp t-exectool -@HAVE_W32_SYSTEM_TRUE@am__append_14 = t-w32-reg +@HAVE_W32_SYSTEM_TRUE@am__append_13 = t-w32-reg +@HAVE_W32_SYSTEM_FALSE@am__append_14 = t-exechelp t-exectool subdir = common ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ - $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ - $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ - $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ - $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ - $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ - $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ - $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ksba.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/ldap.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libassuan.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/npth.m4 \ + $(top_srcdir)/m4/ntbtls.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/socklen.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 $(top_srcdir)/m4/tar-ustar.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -177,9 +176,9 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = w32info-rc.h CONFIG_CLEAN_VPATH_FILES = -@HAVE_W32CE_SYSTEM_FALSE@am__EXEEXT_1 = t-exechelp$(EXEEXT) \ -@HAVE_W32CE_SYSTEM_FALSE@ t-exectool$(EXEEXT) -@HAVE_W32_SYSTEM_TRUE@am__EXEEXT_2 = t-w32-reg$(EXEEXT) +@HAVE_W32_SYSTEM_TRUE@am__EXEEXT_1 = t-w32-reg$(EXEEXT) +@HAVE_W32_SYSTEM_FALSE@am__EXEEXT_2 = t-exechelp$(EXEEXT) \ +@HAVE_W32_SYSTEM_FALSE@ t-exectool$(EXEEXT) am__EXEEXT_3 = t-stringhelp$(EXEEXT) t-timestuff$(EXEEXT) \ t-convert$(EXEEXT) t-percent$(EXEEXT) t-gettime$(EXEEXT) \ t-sysutils$(EXEEXT) t-sexputil$(EXEEXT) t-session-env$(EXEEXT) \ @@ -187,9 +186,8 @@ am__EXEEXT_3 = t-stringhelp$(EXEEXT) t-timestuff$(EXEEXT) \ t-mapstrings$(EXEEXT) t-zb32$(EXEEXT) t-mbox-util$(EXEEXT) \ t-iobuf$(EXEEXT) t-strlist$(EXEEXT) t-name-value$(EXEEXT) \ t-ccparray$(EXEEXT) t-recsel$(EXEEXT) t-w32-cmdline$(EXEEXT) \ - $(am__EXEEXT_1) $(am__EXEEXT_2) -@MAINTAINER_MODE_TRUE@am__EXEEXT_4 = t-helpfile$(EXEEXT) \ -@MAINTAINER_MODE_TRUE@ t-b64$(EXEEXT) + t-b64$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_2) +@MAINTAINER_MODE_TRUE@am__EXEEXT_4 = t-helpfile$(EXEEXT) PROGRAMS = $(noinst_PROGRAMS) LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru @@ -925,10 +923,10 @@ libgpgrl_a_SOURCES = \ 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-name-value \ - t-ccparray t-recsel t-w32-cmdline $(am__append_13) \ + t-ccparray t-recsel t-w32-cmdline t-b64 $(am__append_13) \ $(am__append_14) @MAINTAINER_MODE_FALSE@module_maint_tests = -@MAINTAINER_MODE_TRUE@module_maint_tests = t-helpfile t-b64 +@MAINTAINER_MODE_TRUE@module_maint_tests = t-helpfile t_extra_src = t-support.h t_common_cflags = $(KSBA_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) $(INCICONV) diff --git a/common/b64dec.c b/common/b64dec.c index 6af494b..2904b04 100644 --- a/common/b64dec.c +++ b/common/b64dec.c @@ -16,6 +16,7 @@ * * 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> @@ -252,3 +253,47 @@ b64dec_finish (struct b64state *state) return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 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; + struct b64state state; + size_t nbytes; + char *buffer; + + *r_buffer = NULL; + *r_buflen = 0; + + buffer = xtrystrdup (string); + if (!buffer) + return gpg_error_from_syserror(); + + err = b64dec_start (&state, title); + if (err) + { + xfree (buffer); + return err; + } + b64dec_proc (&state, buffer, strlen (buffer), &nbytes); + err = b64dec_finish (&state); + if (err) + xfree (buffer); + else + { + *r_buffer = buffer; + *r_buflen = nbytes; + } + return err; +} diff --git a/common/b64enc.c b/common/b64enc.c index d633048..7846dcb 100644 --- a/common/b64enc.c +++ b/common/b64enc.c @@ -18,6 +18,7 @@ * * 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> diff --git a/common/compliance.c b/common/compliance.c index eaecee7..bb09d7e 100644 --- a/common/compliance.c +++ b/common/compliance.c @@ -45,6 +45,9 @@ static int module; * using a confue file. */ static unsigned int min_compliant_rsa_length; +/* Temporary hack to allow OCB mode in de-vs mode. */ +static unsigned int vsd_allow_ocb; + /* Return the address of a compliance cache variable for COMPLIANCE. * If no such variable exists NULL is returned. FOR_RNG returns the * cache variable for the RNG compliance check. */ @@ -85,7 +88,9 @@ gnupg_initialize_compliance (int gnupg_module_name) log_assert (! initialized); /* We accept both OpenPGP-style and gcrypt-style algorithm ids. - * Assert that they are compatible. */ + * Assert that they are compatible. At some places gcrypt ids are + * used which can't be encoded in an OpenPGP algo octet; we also + * assert this. */ log_assert ((int) GCRY_PK_RSA == (int) PUBKEY_ALGO_RSA); log_assert ((int) GCRY_PK_RSA_E == (int) PUBKEY_ALGO_RSA_E); log_assert ((int) GCRY_PK_RSA_S == (int) PUBKEY_ALGO_RSA_S); @@ -93,6 +98,9 @@ gnupg_initialize_compliance (int gnupg_module_name) log_assert ((int) GCRY_PK_DSA == (int) PUBKEY_ALGO_DSA); log_assert ((int) GCRY_PK_ECC == (int) PUBKEY_ALGO_ECDH); log_assert ((int) GCRY_PK_ELG == (int) PUBKEY_ALGO_ELGAMAL); + log_assert ((int) GCRY_PK_ECDSA > 255); + log_assert ((int) GCRY_PK_ECDH > 255); + log_assert ((int) GCRY_PK_EDDSA > 255); log_assert ((int) GCRY_CIPHER_NONE == (int) CIPHER_ALGO_NONE); log_assert ((int) GCRY_CIPHER_IDEA == (int) CIPHER_ALGO_IDEA); log_assert ((int) GCRY_CIPHER_3DES == (int) CIPHER_ALGO_3DES); @@ -161,6 +169,9 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: + case GCRY_PK_ECDSA: + case GCRY_PK_ECDH: + case GCRY_PK_EDDSA: algotype = is_ecc; break; @@ -213,7 +224,9 @@ gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, result = (curvename && (algo == PUBKEY_ALGO_ECDH - || algo == PUBKEY_ALGO_ECDSA) + || algo == PUBKEY_ALGO_ECDSA + || algo == GCRY_PK_ECDH + || algo == GCRY_PK_ECDSA) && (!strcmp (curvename, "brainpoolP256r1") || !strcmp (curvename, "brainpoolP384r1") || !strcmp (curvename, "brainpoolP512r1"))); @@ -248,6 +261,13 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, if (! initialized) return 1; + /* Map the the generic ECC algo to ECDSA if requested. */ + if ((algo_flags & PK_ALGO_FLAG_ECC18) + && algo == GCRY_PK_ECC + && (use == PK_USE_VERIFICATION + || use == PK_USE_SIGNING)) + algo = GCRY_PK_ECDSA; + switch (compliance) { case CO_DE_VS: @@ -272,7 +292,6 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, default: log_assert (!"reached"); } - (void)algo_flags; break; case PUBKEY_ALGO_DSA: @@ -293,7 +312,8 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, result = (use == PK_USE_DECRYPTION); break; - case PUBKEY_ALGO_ECDH: + case PUBKEY_ALGO_ECDH: /* Same value as GCRY_PK_ECC, i.e. 18 */ + case GCRY_PK_ECDH: if (use == PK_USE_DECRYPTION) result = 1; else if (use == PK_USE_ENCRYPTION) @@ -318,6 +338,7 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, break; case PUBKEY_ALGO_ECDSA: + case GCRY_PK_ECDSA: if (use == PK_USE_VERIFICATION) result = 1; else @@ -343,6 +364,10 @@ gnupg_pk_is_allowed (enum gnupg_compliance_mode compliance, case PUBKEY_ALGO_EDDSA: + if (use == PK_USE_VERIFICATION) + result = 1; + else /* We may not create such signatures in de-vs mode. */ + result = 0; break; default: @@ -380,7 +405,8 @@ gnupg_cipher_is_compliant (enum gnupg_compliance_mode compliance, switch (module) { case GNUPG_MODULE_NAME_GPG: - return mode == GCRY_CIPHER_MODE_CFB; + return (mode == GCRY_CIPHER_MODE_CFB + || (vsd_allow_ocb && mode == GCRY_CIPHER_MODE_OCB)); case GNUPG_MODULE_NAME_GPGSM: return mode == GCRY_CIPHER_MODE_CBC; } @@ -424,7 +450,8 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, { case GNUPG_MODULE_NAME_GPG: return (mode == GCRY_CIPHER_MODE_NONE - || mode == GCRY_CIPHER_MODE_CFB); + || mode == GCRY_CIPHER_MODE_CFB + || (vsd_allow_ocb && mode == GCRY_CIPHER_MODE_OCB)); case GNUPG_MODULE_NAME_GPGSM: return (mode == GCRY_CIPHER_MODE_NONE || mode == GCRY_CIPHER_MODE_CBC @@ -441,7 +468,8 @@ gnupg_cipher_is_allowed (enum gnupg_compliance_mode compliance, int producer, case CIPHER_ALGO_TWOFISH: return (module == GNUPG_MODULE_NAME_GPG && (mode == GCRY_CIPHER_MODE_NONE - || mode == GCRY_CIPHER_MODE_CFB) + || mode == GCRY_CIPHER_MODE_CFB + || (vsd_allow_ocb && mode == GCRY_CIPHER_MODE_OCB)) && ! producer); default: return 0; @@ -535,6 +563,9 @@ gnupg_rng_is_compliant (enum gnupg_compliance_mode compliance) int *result; int res; + /* #warning debug code ahead */ + /* return 1; */ + result = get_compliance_cache (compliance, 1); if (result && *result != -1) @@ -696,7 +727,15 @@ gnupg_compliance_option_string (enum gnupg_compliance_mode compliance) /* Set additional infos for example taken from config files at startup. */ void -gnupg_set_compliance_extra_info (unsigned int min_rsa) +gnupg_set_compliance_extra_info (enum gnupg_co_extra_infos what, + unsigned int value) { - min_compliant_rsa_length = min_rsa; + switch (what) + { + case CO_EXTRA_INFO_MIN_RSA: + min_compliant_rsa_length = value; + break; + case CO_EXTRA_INFO_VSD_ALLOW_OCB: + vsd_allow_ocb = value; + } } diff --git a/common/compliance.h b/common/compliance.h index e29ff4e..b89cd48 100644 --- a/common/compliance.h +++ b/common/compliance.h @@ -36,20 +36,23 @@ void gnupg_initialize_compliance (int gnupg_module_name); + enum gnupg_compliance_mode { CO_GNUPG, CO_RFC4880, CO_RFC2440, CO_PGP6, CO_PGP7, CO_PGP8, CO_DE_VS }; + enum pk_use_case { PK_USE_ENCRYPTION, PK_USE_DECRYPTION, - PK_USE_SIGNING, PK_USE_VERIFICATION, + PK_USE_SIGNING, PK_USE_VERIFICATION }; /* Flags to distinguish public key algorithm variants. */ #define PK_ALGO_FLAG_RSAPSS 1 /* Use rsaPSS padding. */ +#define PK_ALGO_FLAG_ECC18 256 /* GCRY_PK_ECC is used in a generic way. */ int gnupg_pk_is_compliant (enum gnupg_compliance_mode compliance, int algo, @@ -91,7 +94,14 @@ int gnupg_parse_compliance_option (const char *string, const char *gnupg_compliance_option_string (enum gnupg_compliance_mode compliance); -void gnupg_set_compliance_extra_info (unsigned int min_rsa); +enum gnupg_co_extra_infos + { + CO_EXTRA_INFO_MIN_RSA, + CO_EXTRA_INFO_VSD_ALLOW_OCB + }; + +void gnupg_set_compliance_extra_info (enum gnupg_co_extra_infos what, + unsigned int value); #endif /*GNUPG_COMMON_COMPLIANCE_H*/ diff --git a/common/dotlock.c b/common/dotlock.c index 772bda3..4cd9948 100644 --- a/common/dotlock.c +++ b/common/dotlock.c @@ -1024,6 +1024,40 @@ dotlock_is_locked (dotlock_t h) } +/* 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 @@ -1183,27 +1217,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. */ + + wtimereal = next_wait_interval (&wtime, &timeout); - sumtime += wtime; + sumtime += wtimereal; if (sumtime >= 1500) { sumtime = 0; @@ -1211,9 +1232,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; } @@ -1255,28 +1275,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/exechelp-w32.c b/common/exechelp-w32.c index 00cf3fc..0600bc5 100644 --- a/common/exechelp-w32.c +++ b/common/exechelp-w32.c @@ -550,7 +550,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[], nullhd[1] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)? GetStdHandle (STD_OUTPUT_HANDLE) : w32_open_null (1)); if (errpipe[1] == INVALID_HANDLE_VALUE) - nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDOUT)? + nullhd[2] = ((flags & GNUPG_SPAWN_KEEP_STDERR)? GetStdHandle (STD_ERROR_HANDLE) : w32_open_null (1)); /* Start the process. Note that we can't run the PREEXEC function diff --git a/common/gettime.c b/common/gettime.c index 3fe30ce..d24a4b1 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" @@ -57,6 +62,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 @@ -168,6 +278,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. @@ -204,7 +336,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; } @@ -359,18 +505,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); @@ -382,20 +524,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) @@ -449,41 +619,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 @@ -492,7 +627,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) { @@ -528,24 +667,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); @@ -768,7 +890,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 73f1886..e216ddd 100644 --- a/common/gettime.h +++ b/common/gettime.h @@ -32,12 +32,22 @@ #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. */ typedef char gnupg_isotime_t[16]; +/* Constant string of 16-byte, which is compatible to the type + gnupg_iso_time_t. */ +#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); @@ -46,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/homedir.c b/common/homedir.c index 9788c22..0a8e55f 100644 --- a/common/homedir.c +++ b/common/homedir.c @@ -177,6 +177,10 @@ copy_dir_with_fixup (const char *newdir) { char *result = NULL; char *p; +#ifdef HAVE_W32_SYSTEM + char *p0; + const char *s; +#endif if (!*newdir) return NULL; @@ -208,6 +212,29 @@ copy_dir_with_fixup (const char *newdir) *p-- = 0; } + /* Hack to mitigate badly doubled backslashes. */ + s = result? result : newdir; + if (s[0] == '\\' && s[1] == '\\' && s[2] != '\\') + { + /* UNC (\\Servername\file) or Long UNC (\\?\Servername\file) + * Does not seem to be double quoted. */ + } + else if (strstr (s, "\\\\")) + { + /* Double quotes detected. Fold them into one because that is + * what what Windows does. This way we get a unique hash + * regardless of the number of doubled backslashes. */ + if (!result) + result = xstrdup (newdir); + for (p0=p=result; *p; p++) + { + *p0++ = *p; + while (*p == '\\' && p[1] == '\\') + p++; + } + *p0 = 0; + } + #else /*!HAVE_W32_SYSTEM*/ if (newdir[strlen (newdir)-1] == '/') diff --git a/common/init.c b/common/init.c index 4ae7cbc..93545c8 100644 --- a/common/init.c +++ b/common/init.c @@ -30,6 +30,9 @@ #include <config.h> #ifdef HAVE_W32_SYSTEM +# if _WIN32_WINNT < 0x0600 +# define _WIN32_WINNT 0x0600 /* Required for SetProcessDEPPolicy. */ +# endif # ifdef HAVE_WINSOCK2_H # include <winsock2.h> # endif @@ -239,7 +242,21 @@ _init_common_subsystems (gpg_err_source_t errsource, int *argcp, char ***argvp) log_set_socket_dir_cb (gnupg_socketdir); #if HAVE_W32_SYSTEM - /* For Standard Windows we use our own parser for the command line + /* Make sure that Data Execution Prevention is enabled. */ + if (GetSystemDEPPolicy () >= 2) + { + DWORD flags; + BOOL perm; + + if (!GetProcessDEPPolicy (GetCurrentProcess (), &flags, &perm)) + log_info ("error getting DEP policy: %s\n", + w32_strerror (GetLastError())); + else if (!(flags & PROCESS_DEP_ENABLE) + && !SetProcessDEPPolicy (PROCESS_DEP_ENABLE)) + log_info ("Warning: Enabling DEP failed: %s (%d,%d)\n", + w32_strerror (GetLastError ()), (int)flags, (int)perm); + } + /* On Windows we use our own parser for the command line * so that we can return an array of utf-8 encoded strings. */ prepare_w32_commandline (argcp, argvp); #else diff --git a/common/iobuf.c b/common/iobuf.c index 6370efb..2752e39 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -1,7 +1,7 @@ /* iobuf.c - File Handling for OpenPGP. * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008, * 2009, 2010, 2011 Free Software Foundation, Inc. - * Copyright (C) 2015 g10 Code GmbH + * Copyright (C) 2015, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -27,6 +27,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #include <config.h> @@ -35,7 +36,6 @@ #include <string.h> #include <errno.h> #include <ctype.h> -#include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -93,6 +93,9 @@ typedef struct int no_cache; int eof_seen; int print_only_name; /* Flags indicating that fname is not a real file. */ + char peeked[32]; /* Read ahead buffer. */ + byte npeeked; /* Number of bytes valid in peeked. */ + byte upeeked; /* Number of bytes used from peeked. */ char fname[1]; /* Name of the file. */ } file_filter_ctx_t; @@ -203,7 +206,7 @@ fd_cache_invalidate (const char *fname) close_cache_t cc; int rc = 0; - assert (fname); + log_assert (fname); if (DBG_IOBUF) log_debug ("fd_cache_invalidate (%s)\n", fname); @@ -366,7 +369,7 @@ fd_cache_close (const char *fname, gnupg_fd_t fp) { close_cache_t cc; - assert (fp); + log_assert (fp); if (!fname || !*fname) { #ifdef HAVE_W32_SYSTEM @@ -407,7 +410,7 @@ fd_cache_open (const char *fname, const char *mode) { close_cache_t cc; - assert (fname); + log_assert (fname); for (cc = close_cache; cc; cc = cc->next) { if (cc->fp != GNUPG_INVALID_FD && !fd_cache_strcmp (cc->fname, fname)) @@ -453,8 +456,17 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf, if (control == IOBUFCTRL_UNDERFLOW) { - assert (size); /* We need a buffer. */ - if (a->eof_seen) + log_assert (size); /* We need a buffer. */ + if (a->npeeked > a->upeeked) + { + nbytes = a->npeeked - a->upeeked; + if (nbytes > size) + nbytes = size; + memcpy (buf, a->peeked + a->upeeked, nbytes); + a->upeeked += nbytes; + *ret_len = nbytes; + } + else if (a->eof_seen) { rc = -1; *ret_len = 0; @@ -471,7 +483,8 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf, if (ec != ERROR_BROKEN_PIPE) { rc = gpg_error_from_errno (ec); - log_error ("%s: read error: ec=%d\n", a->fname, ec); + log_error ("%s: read error: %s (ec=%d)\n", + a->fname, gpg_strerror (rc), ec); } } else if (!nread) @@ -529,9 +542,10 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf, { if (size && !WriteFile (f, p, nbytes, &n, NULL)) { - int ec = (int) GetLastError (); - rc = gpg_error_from_errno (ec); - log_error ("%s: write error: ec=%d\n", a->fname, ec); + int ec = gnupg_w32_set_errno (-1); + rc = gpg_error_from_syserror (); + log_error ("%s: write error: %s (ec=%d)\n", + a->fname, gpg_strerror (rc), ec); break; } p += n; @@ -573,6 +587,69 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf, a->eof_seen = 0; a->keep_open = 0; a->no_cache = 0; + a->npeeked = 0; + a->upeeked = 0; + } + else if (control == IOBUFCTRL_PEEK) + { + /* Peek on the input. */ +#ifdef HAVE_W32_SYSTEM + unsigned long nread; + + nbytes = 0; + if (!ReadFile (f, a->peeked, sizeof a->peeked, &nread, NULL)) + { + int ec = (int) GetLastError (); + if (ec != ERROR_BROKEN_PIPE) + { + rc = gpg_error_from_errno (ec); + log_error ("%s: read error: %s (ec=%d)\n", + a->fname, gpg_strerror (rc), ec); + } + a->npeeked = 0; + } + else if (!nread) + { + a->eof_seen = 1; + a->npeeked = 0; + } + else + { + a->npeeked = nread; + } + +#else /* Unix */ + + int n; + + peek_more: + do + { + n = read (f, a->peeked + a->npeeked, sizeof a->peeked - a->npeeked); + } + while (n == -1 && errno == EINTR); + if (n > 0) + { + a->npeeked += n; + if (a->npeeked < sizeof a->peeked) + goto peek_more; + } + else if (!n) /* eof */ + { + a->eof_seen = 1; + } + else /* error */ + { + rc = gpg_error_from_syserror (); + if (gpg_err_code (rc) != GPG_ERR_EPIPE) + log_error ("%s: read error: %s\n", a->fname, gpg_strerror (rc)); + } +#endif /* Unix */ + + size = a->npeeked < size? a->npeeked : size; + memcpy (buf, a->peeked, size); + *ret_len = size; + rc = 0; /* Return success - the user needs to check ret_len. */ } else if (control == IOBUFCTRL_DESC) { @@ -609,7 +686,7 @@ file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf, if (control == IOBUFCTRL_UNDERFLOW) { - assert (size); /* We need a buffer. */ + log_assert (size); /* We need a buffer. */ if (a->eof_seen) { rc = -1; @@ -700,7 +777,7 @@ sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, if (control == IOBUFCTRL_UNDERFLOW) { - assert (size); /* need a buffer */ + log_assert (size); /* need a buffer */ if (a->eof_seen) { rc = -1; @@ -743,7 +820,8 @@ sock_filter (void *opaque, int control, iobuf_t chain, byte * buf, if (n == SOCKET_ERROR) { int ec = (int) WSAGetLastError (); - rc = gpg_error_from_errno (ec); + gnupg_w32_set_errno (ec); + rc = gpg_error_from_syserror (); log_error ("socket write error: ec=%d\n", ec); break; } @@ -795,7 +873,7 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, size_t n = 0; p = buf; - assert (size); /* need a buffer */ + log_assert (size); /* need a buffer */ if (a->eof) /* don't read any further */ rc = -1; while (!rc && size) @@ -917,7 +995,7 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, { /* the complicated openpgp scheme */ size_t blen, n, nbytes = size + a->buflen; - assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); + log_assert (a->buflen <= OP_MIN_PARTIAL_CHUNK); if (nbytes < OP_MIN_PARTIAL_CHUNK) { /* not enough to write a partial block out; so we store it */ @@ -941,12 +1019,12 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, blen /= 2; c--; /* write the partial length header */ - assert (c <= 0x1f); /*;-) */ + log_assert (c <= 0x1f); /*;-) */ c |= 0xe0; iobuf_put (chain, c); if ((n = a->buflen)) { /* write stuff from the buffer */ - assert (n == OP_MIN_PARTIAL_CHUNK); + log_assert (n == OP_MIN_PARTIAL_CHUNK); if (iobuf_write (chain, a->buffer, n)) rc = gpg_error_from_syserror (); a->buflen = 0; @@ -963,8 +1041,8 @@ block_filter (void *opaque, int control, iobuf_t chain, byte * buffer, /* store the rest in the buffer */ if (!rc && nbytes) { - assert (!a->buflen); - assert (nbytes < OP_MIN_PARTIAL_CHUNK); + log_assert (!a->buflen); + log_assert (nbytes < OP_MIN_PARTIAL_CHUNK); if (!a->buffer) a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK); memcpy (a->buffer, p, nbytes); @@ -1102,8 +1180,8 @@ iobuf_alloc (int use, size_t bufsize) iobuf_t a; static int number = 0; - assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP - || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP); + log_assert (use == IOBUF_INPUT || use == IOBUF_INPUT_TEMP + || use == IOBUF_OUTPUT || use == IOBUF_OUTPUT_TEMP); if (bufsize == 0) { log_bug ("iobuf_alloc() passed a bufsize of 0!\n"); @@ -1218,7 +1296,7 @@ iobuf_temp_with_content (const char *buffer, size_t length) int i; a = iobuf_alloc (IOBUF_INPUT_TEMP, length); - assert (length == a->d.size); + log_assert (length == a->d.size); /* memcpy (a->d.buf, buffer, length); */ for (i=0; i < length; i++) a->d.buf[i] = buffer[i]; @@ -1249,7 +1327,7 @@ do_open (const char *fname, int special_filenames, int fd; byte desc[MAX_IOBUF_DESC]; - assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT); + log_assert (use == IOBUF_INPUT || use == IOBUF_OUTPUT); if (special_filenames /* NULL or '-'. */ @@ -1487,6 +1565,25 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval) return fd_cache_synchronize (ptrval); } } + else if (cmd == IOBUF_IOCTL_PEEK) + { + /* Peek at a justed opened file. Use this only directly after a + * file has been opened for reading. Don't use it after you did + * a seek. This works only if just file filter has been + * pushed. Expects a buffer wit size INTVAL at PTRVAL and returns + * the number of bytes put into the buffer. */ + if (DBG_IOBUF) + log_debug ("iobuf-%d.%d: ioctl '%s' peek\n", + a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc)); + if (a->filter == file_filter && ptrval && intval) + { + file_filter_ctx_t *fcx = a->filter_ov; + size_t len = intval; + + if (!file_filter (fcx, IOBUFCTRL_PEEK, NULL, ptrval, &len)) + return (int)len; + } + } return -1; @@ -1666,13 +1763,13 @@ iobuf_pop_filter (iobuf_t a, int (*f) (void *opaque, int control, if (a->use == IOBUF_INPUT_TEMP || a->use == IOBUF_OUTPUT_TEMP) { /* This should be the last filter in the pipeline. */ - assert (! a->chain); + log_assert (! a->chain); return 0; } if (!a->filter) { /* this is simple */ b = a->chain; - assert (b); + log_assert (b); xfree (a->d.buf); xfree (a->real_fname); memcpy (a, b, sizeof *a); @@ -1767,12 +1864,12 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target) buffer. */ return -1; - assert (a->use == IOBUF_INPUT); + log_assert (a->use == IOBUF_INPUT); /* If there is still some buffered data, then move it to the start of the buffer and try to fill the end of the buffer. (This is useful if we are called from iobuf_peek().) */ - assert (a->d.start <= a->d.len); + log_assert (a->d.start <= a->d.len); a->d.len -= a->d.start; memmove (a->d.buf, &a->d.buf[a->d.start], a->d.len); a->d.start = 0; @@ -1900,7 +1997,7 @@ underflow_target (iobuf_t a, int clear_pending_eof, size_t target) } } - assert (a->d.start <= a->d.len); + log_assert (a->d.start <= a->d.len); if (a->d.start < a->d.len) return a->d.buf[a->d.start++]; @@ -1957,7 +2054,7 @@ iobuf_readbyte (iobuf_t a) return -1; } - assert (a->d.start <= a->d.len); + log_assert (a->d.start <= a->d.len); if (a->nlimit && a->nbytes >= a->nlimit) return -1; /* forced EOF */ @@ -1969,7 +2066,7 @@ iobuf_readbyte (iobuf_t a) else if ((c = underflow (a, 1)) == -1) return -1; /* EOF */ - assert (a->d.start <= a->d.len); + log_assert (a->d.start <= a->d.len); /* Note: if underflow doesn't return EOF, then it returns the first byte that was read and advances a->d.start appropriately. */ @@ -2057,8 +2154,8 @@ iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) { int n = 0; - assert (buflen > 0); - assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP); + log_assert (buflen > 0); + log_assert (a->use == IOBUF_INPUT || a->use == IOBUF_INPUT_TEMP); if (buflen > a->d.size) /* We can't peek more than we can buffer. */ @@ -2074,7 +2171,7 @@ iobuf_peek (iobuf_t a, byte * buf, unsigned buflen) /* Underflow consumes the first character (it's the return value). unget() it by resetting the "file position". */ - assert (a->d.start == 1); + log_assert (a->d.start == 1); a->d.start = 0; } @@ -2109,7 +2206,7 @@ iobuf_writebyte (iobuf_t a, unsigned int c) if ((rc=filter_flush (a))) return rc; - assert (a->d.len < a->d.size); + log_assert (a->d.len < a->d.size); a->d.buf[a->d.len++] = c; return 0; } @@ -2168,8 +2265,8 @@ iobuf_writestr (iobuf_t a, const char *buf) int iobuf_write_temp (iobuf_t dest, iobuf_t source) { - assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); - assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP); + log_assert (source->use == IOBUF_OUTPUT || source->use == IOBUF_OUTPUT_TEMP); + log_assert (dest->use == IOBUF_OUTPUT || dest->use == IOBUF_OUTPUT_TEMP); iobuf_flush_temp (source); return iobuf_write (dest, source->d.buf, source->d.len); @@ -2269,13 +2366,10 @@ iobuf_set_limit (iobuf_t a, off_t nlimit) } - -off_t -iobuf_get_filelength (iobuf_t a, int *overflow) +/* Return the length of the file behind A. If there is no file, return 0. */ +uint64_t +iobuf_get_filelength (iobuf_t a) { - if (overflow) - *overflow = 0; - /* Hmmm: file_filter may have already been removed */ for ( ; a->chain; a = a->chain ) ; @@ -2288,56 +2382,18 @@ iobuf_get_filelength (iobuf_t a, int *overflow) gnupg_fd_t fp = b->fp; #if defined(HAVE_W32_SYSTEM) - ulong size; - static int (* __stdcall get_file_size_ex) (void *handle, - LARGE_INTEGER *r_size); - static int get_file_size_ex_initialized; - - if (!get_file_size_ex_initialized) - { - void *handle; - - handle = dlopen ("kernel32.dll", RTLD_LAZY); - if (handle) - { - get_file_size_ex = dlsym (handle, "GetFileSizeEx"); - if (!get_file_size_ex) - dlclose (handle); - } - get_file_size_ex_initialized = 1; - } + LARGE_INTEGER exsize; - if (get_file_size_ex) - { - /* This is a newer system with GetFileSizeEx; we use this - then because it seem that GetFileSize won't return a - proper error in case a file is larger than 4GB. */ - LARGE_INTEGER exsize; - - if (get_file_size_ex (fp, &exsize)) - { - if (!exsize.u.HighPart) - return exsize.u.LowPart; - if (overflow) - *overflow = 1; - return 0; - } - } - else - { - if ((size=GetFileSize (fp, NULL)) != 0xffffffff) - return size; - } + if (GetFileSizeEx (fp, &exsize)) + return exsize.QuadPart; log_error ("GetFileSize for handle %p failed: %s\n", fp, w32_strerror (-1)); #else /*!HAVE_W32_SYSTEM*/ - { - struct stat st; + struct stat st; - if ( !fstat (FD2INT (fp), &st) ) - return st.st_size; - log_error("fstat() failed: %s\n", strerror(errno) ); - } + if ( !fstat (fp, &st) ) + return st.st_size; + log_error("fstat() failed: %s\n", strerror(errno) ); #endif /*!HAVE_W32_SYSTEM*/ } @@ -2552,7 +2608,7 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, NUL character in the buffer. This requires at least 2 bytes. We don't complicate the code by handling the stupid corner case, but simply assert that it can't happen. */ - assert (!buffer || length >= 2 || maxlen >= 2); + log_assert (!buffer || length >= 2 || maxlen >= 2); if (!buffer || length <= 1) /* must allocate a new buffer */ @@ -2585,7 +2641,7 @@ iobuf_read_line (iobuf_t a, byte ** addr_of_buffer, /* p is pointing at the last byte in the buffer. We always terminate the line with "\n\0" so overwrite the previous byte with a \n. */ - assert (p > buffer); + log_assert (p > buffer); p[-1] = '\n'; /* Indicate truncation. */ diff --git a/common/iobuf.h b/common/iobuf.h index 624e154..9dd245a 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -106,6 +106,7 @@ enum IOBUFCTRL_FLUSH = 4, IOBUFCTRL_DESC = 5, IOBUFCTRL_CANCEL = 6, + IOBUFCTRL_PEEK = 7, IOBUFCTRL_USER = 16 }; @@ -116,7 +117,8 @@ typedef enum IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */ IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */ IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */ - IOBUF_IOCTL_FSYNC = 4 /* Uses ptrval. */ + IOBUF_IOCTL_FSYNC = 4, /* Uses ptrval. */ + IOBUF_IOCTL_PEEK = 5 /* Uses intval and ptrval. */ } iobuf_ioctl_t; enum iobuf_use @@ -554,12 +556,8 @@ size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen); size_t iobuf_copy (iobuf_t dest, iobuf_t source); /* Return the size of any underlying file. This only works with - file_filter based pipelines. - - On Win32, it is sometimes not possible to determine the size of - files larger than 4GB. In this case, *OVERFLOW (if not NULL) is - set to 1. Otherwise, *OVERFLOW is set to 0. */ -off_t iobuf_get_filelength (iobuf_t a, int *overflow); + file_filter based pipelines. */ +uint64_t iobuf_get_filelength (iobuf_t a); #define IOBUF_FILELENGTH_LIMIT 0xffffffff /* Return the file descriptor designating the underlying file. This diff --git a/common/ksba-io-support.c b/common/ksba-io-support.c index 2832a4f..352485f 100644 --- a/common/ksba-io-support.c +++ b/common/ksba-io-support.c @@ -1,6 +1,6 @@ /* kska-io-support.c - Supporting functions for ksba reader and writer * Copyright (C) 2001-2005, 2007, 2010-2011, 2017 Werner Koch - * Copyright (C) 2006 g10 Code GmbH + * Copyright (C) 2006, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -26,6 +26,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #include <config.h> @@ -40,6 +41,7 @@ #include "util.h" #include "i18n.h" +#include "tlv.h" #include "ksba-io-support.h" @@ -65,6 +67,12 @@ struct reader_cb_parm_s int autodetect; /* Try to detect the input encoding. */ int assume_pem; /* Assume input encoding is PEM. */ int assume_base64; /* Assume input is base64 encoded. */ + int strip_zeroes; /* Expect a SEQUENCE followed by zero padding. */ + /* 1 = check state; 2 = reading; 3 = checking */ + /* for zeroes. */ + int use_maxread; /* If true read not more than MAXREAD. */ + unsigned int maxread; /* # of bytes left to read. */ + off_t nzeroes; /* Number of padding zeroes red. */ int identified; int is_pem; @@ -89,6 +97,15 @@ struct writer_cb_parm_s char *pem_name; /* Malloced. */ + struct { + gnupg_ksba_progress_cb_t cb; + ctrl_t ctrl; + u32 last_time; /* last time reported */ + uint64_t last; /* last amount reported */ + uint64_t current; /* current amount */ + uint64_t total; /* total amount */ + } progress; + int wrote_begin; int did_finish; @@ -103,6 +120,7 @@ struct writer_cb_parm_s /* Context for this module's functions. */ struct gnupg_ksba_io_s { + int is_writer; /* True if this context refers a writer object. */ union { struct reader_cb_parm_s rparm; struct writer_cb_parm_s wparm; @@ -390,6 +408,55 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) } +/* Read up to 10 bytes to test whether the data consist of a sequence; + * if that is true, set the limited flag and record the length of the + * entire sequence in PARM. Unget everything then. Return true if we + * have a sequence with a fixed length. */ +static int +starts_with_sequence (struct reader_cb_parm_s *parm) +{ + gpg_error_t err; + unsigned char peekbuf[10]; + int npeeked, c; + int found = 0; + const unsigned char *p; + size_t n, objlen, hdrlen; + int class, tag, constructed, ndef; + + for (npeeked=0; npeeked < sizeof peekbuf; npeeked++) + { + c = es_getc (parm->fp); + if (c == EOF) + goto leave; + peekbuf[npeeked] = c; + } + /* Enough to check for a sequence. */ + + p = peekbuf; + n = npeeked; + err = parse_ber_header (&p, &n, &class, &tag, &constructed, + &ndef, &objlen, &hdrlen); + if (err) + { + log_debug ("%s: error parsing data: %s\n", __func__, gpg_strerror (err)); + goto leave; + } + + if (class == CLASS_UNIVERSAL && constructed && tag == TAG_SEQUENCE && !ndef) + { + /* We need to add 1 due to the way we implement the limit. */ + parm->maxread = objlen + hdrlen + 1; + if (!(parm->maxread < objlen + hdrlen) && parm->maxread) + parm->use_maxread = 1; + found = 1; + } + + leave: + while (npeeked) + es_ungetc (peekbuf[--npeeked], parm->fp); + return found; +} + static int simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) @@ -402,9 +469,55 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) if (!buffer) return -1; /* not supported */ + restart: + if (parm->strip_zeroes) + { + if (parm->strip_zeroes == 1) + { + if (starts_with_sequence (parm)) + parm->strip_zeroes = 2; /* Found fixed length sequence. */ + else + parm->strip_zeroes = 0; /* Disable zero padding check. */ + } + else if (parm->strip_zeroes == 3) + { + /* Limit reached - check that only zeroes follow. */ + while (!(c = es_getc (parm->fp))) + parm->nzeroes++; + if (c == EOF) + { /* only zeroes found. Reset zero padding engine and + * return EOF. */ + parm->strip_zeroes = 0; + parm->eof_seen = 1; + return -1; + } + /* Not only zeroes. Reset engine and continue. */ + parm->strip_zeroes = 0; + } + } + for (n=0; n < count; n++) { - c = es_getc (parm->fp); + if (parm->use_maxread && !--parm->maxread) + { + parm->use_maxread = 0; + if (parm->strip_zeroes) + { + parm->strip_zeroes = 3; + parm->nzeroes = 0; + if (n) + goto leave; /* Return what we already got. */ + goto restart; /* Immediately check for trailing zeroes. */ + } + } + + if (parm->nzeroes) + { + parm->nzeroes--; + c = 0; + } + else + c = es_getc (parm->fp); if (c == EOF) { parm->eof_seen = 1; @@ -417,6 +530,7 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) *(byte *)buffer++ = c; } + leave: *nread = n; return 0; } @@ -424,6 +538,33 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread) +/* Call the progress callback if its time. We do this very 2 seconds + * or if FORCE is set. However, we also require that at least 64KiB + * have been written to avoid unnecessary progress lines for small + * files. */ +static gpg_error_t +update_write_progress (struct writer_cb_parm_s *parm, size_t count, int force) +{ + gpg_error_t err = 0; + u32 timestamp; + + parm->progress.current += count; + if (parm->progress.current >= (64*1024)) + { + timestamp = make_timestamp (); + if (force || (timestamp - parm->progress.last_time > 1)) + { + parm->progress.last = parm->progress.current; + parm->progress.last_time = timestamp; + err = parm->progress.cb (parm->progress.ctrl, + parm->progress.current, + parm->progress.total); + } + } + return err; +} + + static int base64_writer_cb (void *cb_value, const void *buffer, size_t count) { @@ -432,6 +573,8 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count) int i, c, idx, quad_count; const unsigned char *p; estream_t stream = parm->stream; + int rc; + size_t nleft; if (!count) return 0; @@ -454,7 +597,7 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count) for (i=0; i < idx; i++) radbuf[i] = parm->base64.radbuf[i]; - for (p=buffer; count; p++, count--) + for (p=buffer, nleft = count; nleft; p++, nleft--) { radbuf[idx++] = *p; if (idx > 2) @@ -480,7 +623,11 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count) parm->base64.idx = idx; parm->base64.quad_count = quad_count; - return es_ferror (stream)? gpg_error_from_syserror () : 0; + rc = es_ferror (stream)? gpg_error_from_syserror () : 0; + /* Note that we use the unencoded count for the progress. */ + if (!rc && parm->progress.cb) + rc = update_write_progress (parm, count, 0); + return rc; } @@ -491,13 +638,16 @@ plain_writer_cb (void *cb_value, const void *buffer, size_t count) { struct writer_cb_parm_s *parm = cb_value; estream_t stream = parm->stream; + int rc; if (!count) return 0; es_write (stream, buffer, count, NULL); - - return es_ferror (stream)? gpg_error_from_syserror () : 0; + rc = es_ferror (stream)? gpg_error_from_syserror () : 0; + if (!rc && parm->progress.cb) + rc = update_write_progress (parm, count, 0); + return rc; } @@ -507,6 +657,7 @@ base64_finish_write (struct writer_cb_parm_s *parm) unsigned char *radbuf; int c, idx, quad_count; estream_t stream = parm->stream; + int rc; if (!parm->wrote_begin) return 0; /* Nothing written or we are not called in base-64 mode. */ @@ -553,7 +704,10 @@ base64_finish_write (struct writer_cb_parm_s *parm) es_fputs ("-----\n", stream); } - return es_ferror (stream)? gpg_error_from_syserror () : 0; + rc = es_ferror (stream)? gpg_error_from_syserror () : 0; + if (!rc && parm->progress.cb) + rc = update_write_progress (parm, 0, 1); + return rc; } @@ -575,6 +729,7 @@ base64_finish_write (struct writer_cb_parm_s *parm) * GNUPG_KSBA_IO_MULTIPEM - The reader expects that the caller uses * ksba_reader_clear after EOF until no more * objects were found. + * GNUPG_KSBA_IO_STRIP - Strip zero padding from some CMS objects. * * Note that the PEM flag has a higher priority than the BASE64 flag * which in turn has a gight priority than the AUTODETECT flag. @@ -592,6 +747,7 @@ gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx, if (!*ctx) return out_of_core (); (*ctx)->u.rparm.allow_multi_pem = !!(flags & GNUPG_KSBA_IO_MULTIPEM); + (*ctx)->u.rparm.strip_zeroes = !!(flags & GNUPG_KSBA_IO_STRIP); rc = ksba_reader_new (&r); if (rc) @@ -683,6 +839,7 @@ gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, unsigned int flags, *ctx = xtrycalloc (1, sizeof **ctx); if (!*ctx) return gpg_error_from_syserror (); + (*ctx)->is_writer = 1; rc = ksba_writer_new (&w); if (rc) @@ -760,3 +917,37 @@ gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx) xfree (ctx->u.wparm.pem_name); xfree (ctx); } + + +/* Set a callback to the writer object. CTRL will be bassed to the + * callback. */ +void +gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx, + gnupg_ksba_progress_cb_t cb, ctrl_t ctrl) +{ + struct writer_cb_parm_s *parm; + + if (!ctx || !ctx->is_writer) + return; /* Currently only supported for writer objects. */ + parm = &ctx->u.wparm; + + parm->progress.cb = cb; + parm->progress.ctrl = ctrl; + parm->progress.last_time = 0; + parm->progress.last = 0; + parm->progress.current = 0; + parm->progress.total = 0; +} + + +/* Update the total count for the progress thingy. */ +void +gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total) +{ + struct writer_cb_parm_s *parm; + + if (!ctx || !ctx->is_writer) + return; /* Currently only supported for writer objects. */ + parm = &ctx->u.wparm; + parm->progress.total = total; +} diff --git a/common/ksba-io-support.h b/common/ksba-io-support.h index e33e0ed..1dbc303 100644 --- a/common/ksba-io-support.h +++ b/common/ksba-io-support.h @@ -25,6 +25,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: (LGPL-3.0-or-later OR GPL-2.0-or-later) */ #ifndef GNUPG_KSBA_IO_SUPPORT_H @@ -36,11 +37,16 @@ #define GNUPG_KSBA_IO_BASE64 2 /* Plain Base64 format. */ #define GNUPG_KSBA_IO_AUTODETECT 4 /* Try to autodetect the format. */ #define GNUPG_KSBA_IO_MULTIPEM 8 /* Allow more than one PEM chunk. */ +#define GNUPG_KSBA_IO_STRIP 16 /* Strip off zero padding. */ /* Context object. */ typedef struct gnupg_ksba_io_s *gnupg_ksba_io_t; +/* Progress callback type. */ +typedef gpg_error_t (*gnupg_ksba_progress_cb_t)(ctrl_t ctrl, + uint64_t current, + uint64_t total); gpg_error_t gnupg_ksba_create_reader (gnupg_ksba_io_t *ctx, @@ -56,10 +62,13 @@ gpg_error_t gnupg_ksba_create_writer (gnupg_ksba_io_t *ctx, const char *pem_name, estream_t stream, ksba_writer_t *r_writer); - gpg_error_t gnupg_ksba_finish_writer (gnupg_ksba_io_t ctx); void gnupg_ksba_destroy_writer (gnupg_ksba_io_t ctx); +void gnupg_ksba_set_progress_cb (gnupg_ksba_io_t ctx, + gnupg_ksba_progress_cb_t cb, ctrl_t ctrl); +void gnupg_ksba_set_total (gnupg_ksba_io_t ctx, uint64_t total); + diff --git a/common/mbox-util.c b/common/mbox-util.c index 76255ba..66f98ae 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 c377554..bda3184 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -466,7 +466,7 @@ decode_c_string (const char *src) /* Check whether (BUF,LEN) is valid header for an OpenPGP compressed * packet. LEN should be at least 6. */ static int -is_openpgp_compressed_packet (unsigned char *buf, size_t len) +is_openpgp_compressed_packet (const unsigned char *buf, size_t len) { int c, ctb, pkttype; int lenbytes; @@ -508,63 +508,64 @@ is_openpgp_compressed_packet (unsigned char *buf, size_t len) /* - * Check if the file is compressed. + * Check if the file is compressed. You need to pass the first bytes + * of the file as (BUF,BUFLEN). Returns true if the buffer seems to + * be compressed. */ int -is_file_compressed (const char *s, int *ret_rc) +is_file_compressed (const byte *buf, unsigned int buflen) { - iobuf_t a; - byte buf[6]; - int i; - int rc = 0; - int overflow; - - struct magic_compress_s { - size_t len; - byte magic[4]; - } magic[] = { - { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ - { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ - { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ - }; - - if ( iobuf_is_pipe_filename (s) || !ret_rc ) - return 0; /* We can't check stdin or no file was given */ - - a = iobuf_open( s ); - if ( a == NULL ) { - *ret_rc = gpg_error_from_syserror (); - return 0; - } - iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL); - - if ( iobuf_get_filelength( a, &overflow ) < 6 && !overflow) { - *ret_rc = 0; - goto leave; - } - - if ( iobuf_read( a, buf, 6 ) == -1 ) { - *ret_rc = a->error; - goto leave; + int i; + + struct magic_compress_s + { + byte len; + byte extchk; + byte magic[5]; + } magic[] = + { + { 3, 0, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ + { 3, 0, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ + { 4, 0, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ + { 5, 0, { '%', 'P', 'D', 'F', '-'} }, /* PDF */ + { 4, 1, { 0xff, 0xd8, 0xff, 0xe0 } }, /* Maybe JFIF */ + { 5, 2, { 0x89, 'P','N','G', 0x0d} } /* Likely PNG */ + }; + + if ( buflen < 6 ) + { + return 0; /* Too short to check - assume uncompressed. */ } - for ( i = 0; i < DIM( magic ); i++ ) { - if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) { - *ret_rc = 0; - rc = 1; - goto leave; + for ( i = 0; i < DIM (magic); i++ ) + { + if (!memcmp( buf, magic[i].magic, magic[i].len)) + { + switch (magic[i].extchk) + { + case 0: + return 1; /* Is compressed. */ + case 1: + if (buflen > 11 && !memcmp (buf + 6, "JFIF", 5)) + return 1; /* JFIF: this likely a compressed JPEG. */ + break; + case 2: + if (buflen > 8 + && buf[5] == 0x0a && buf[6] == 0x1a && buf[7] == 0x0a) + return 1; /* This is a PNG. */ + break; + default: + break; + } } } - if (is_openpgp_compressed_packet (buf, 6)) - { - *ret_rc = 0; - rc = 1; - } + if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen)) + { + return 1; /* Already compressed. */ + } - leave: - iobuf_close( a ); - return rc; + return 0; /* Not detected as compressed. */ } diff --git a/common/mischelp.c b/common/mischelp.c index ee85002..ef70c9d 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 bdee5a4..7359ec2 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/name-value.c b/common/name-value.c index b9b13d1..f7fefa9 100644 --- a/common/name-value.c +++ b/common/name-value.c @@ -48,6 +48,7 @@ struct name_value_container struct name_value_entry *first; struct name_value_entry *last; unsigned int private_key_mode:1; + unsigned int modified:1; }; @@ -87,11 +88,15 @@ my_error (gpg_err_code_t ec) /* Allocation and deallocation. */ -/* Allocate a private key container structure. */ +/* Allocate a name value container structure. */ nvc_t nvc_new (void) { - return xtrycalloc (1, sizeof (struct name_value_container)); + nvc_t nvc; + nvc = xtrycalloc (1, sizeof (struct name_value_container)); + if (nvc) + nvc->modified = 1; + return nvc; } @@ -142,6 +147,24 @@ nvc_release (nvc_t pk) xfree (pk); } + +/* Return the modified-flag of the container and clear it if CLEAR is + * set. That flag is set for a new container and set with each + * update. */ +int +nvc_modified (nvc_t pk, int clear) +{ + int modified; + + if (!pk) + return 0; + modified = pk->modified; + if (clear) + pk->modified = 0; + return modified; +} + + /* Dealing with names and values. */ @@ -427,6 +450,8 @@ _nvc_add (nvc_t pk, char *name, char *value, strlist_t raw_value, else pk->first = pk->last = e; + pk->modified = 1; + leave: if (err) { @@ -476,21 +501,29 @@ nvc_set (nvc_t pk, const char *name, const char *value) e = nvc_lookup (pk, name); if (e) - return nve_set (e, value); + return nve_set (pk, e, value); else return nvc_add (pk, name, value); } -/* Update entry E to VALUE. */ +/* Update entry E to VALUE. PK is optional; if given its modified + * flag will be updated. */ gpg_error_t -nve_set (nve_t e, const char *value) +nve_set (nvc_t pk, nve_t e, const char *value) { char *v; if (!e) return GPG_ERR_INV_ARG; + if (e->value && value && !strcmp (e->value, value)) + { + /* Setting same value - ignore this call and don't set the + * modified flag. */ + return 0; + } + v = xtrystrdup (value? value:""); if (!v) return my_error_from_syserror (); @@ -501,6 +534,8 @@ nve_set (nve_t e, const char *value) wipememory (e->value, strlen (e->value)); xfree (e->value); e->value = v; + if (pk) + pk->modified = 1; return 0; } @@ -521,6 +556,7 @@ nvc_delete (nvc_t pk, nve_t entry) pk->last = entry->prev; nve_release (entry, pk->private_key_mode); + pk->modified = 1; } /* Delete the entries with NAME from PK. */ diff --git a/common/name-value.h b/common/name-value.h index fd0a98c..1d5d795 100644 --- a/common/name-value.h +++ b/common/name-value.h @@ -50,6 +50,9 @@ nvc_t nvc_new_private_key (void); /* Release a name value container structure. */ void nvc_release (nvc_t pk); +/* Return the modified flag and optionally clear it. */ +int nvc_modified (nvc_t pk, int clear); + /* Get the name. */ char *nve_name (nve_t pke); @@ -91,8 +94,8 @@ gpg_error_t nvc_add (nvc_t pk, const char *name, const char *value); first entry is updated. */ gpg_error_t nvc_set (nvc_t pk, const char *name, const char *value); -/* Update entry E to VALUE. */ -gpg_error_t nve_set (nve_t e, const char *value); +/* Update entry E to VALUE. PK is optional. */ +gpg_error_t nve_set (nvc_t pk, nve_t e, const char *value); /* Delete the given entry from PK. */ void nvc_delete (nvc_t pk, nve_t pke); diff --git a/common/openpgp-oid.c b/common/openpgp-oid.c index 943fe3e..1e5dc4a 100644 --- a/common/openpgp-oid.c +++ b/common/openpgp-oid.c @@ -71,6 +71,21 @@ static const char oid_ed25519[] = static const char oid_cv25519[] = { 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01 }; +/* A table to store keyalgo strings like "rsa2048 or "ed25519" so that + * we do not need to allocate them. This is currently a simple array + * but may eventually be changed to a fast data structure. Noet that + * unknown algorithms are stored with (NBITS,CURVE) set to (0,NULL). */ +struct keyalgo_string_s +{ + enum gcry_pk_algos algo; /* Mandatory. */ + unsigned int nbits; /* Size for classical algos. */ + char *curve; /* Curvename (OID) or NULL. */ + char *name; /* Allocated name. */ +}; +static struct keyalgo_string_s *keyalgo_strings; /* The table. */ +static size_t keyalgo_strings_size; /* Allocated size. */ +static size_t keyalgo_strings_used; /* Used size. */ + /* Helper for openpgp_oid_from_str. */ static size_t @@ -383,9 +398,9 @@ openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo) } -/* Map an OpenPGP OID to the Libgcrypt curve NAME. Returns NULL for - unknown curve names. Unless CANON is set we prefer an alias name - here which is more suitable for printing. */ +/* Map an OpenPGP OID to the Libgcrypt curve name. Returns NULL for + * unknown curve names. Unless CANON is set we prefer an alias name + * here which is more suitable for printing. */ const char * openpgp_oid_to_curve (const char *oidstr, int canon) { @@ -402,6 +417,27 @@ openpgp_oid_to_curve (const char *oidstr, int canon) } +/* Map an OpenPGP OID, name or alias to the Libgcrypt curve name. + * Returns NULL for unknown curve names. Unless CANON is set we + * prefer an alias name here which is more suitable for printing. */ +const char * +openpgp_oid_or_name_to_curve (const char *oidname, int canon) +{ + int i; + + if (!oidname) + return NULL; + + for (i=0; oidtable[i].name; i++) + if (!strcmp (oidtable[i].oidstr, oidname) + || !strcmp (oidtable[i].name, oidname) + || (oidtable[i].alias &&!strcmp (oidtable[i].alias, oidname))) + return !canon && oidtable[i].alias? oidtable[i].alias : oidtable[i].name; + + return NULL; +} + + /* Return true if the curve with NAME is supported. */ static int curve_supported_p (const char *name) @@ -470,3 +506,126 @@ openpgp_is_curve_supported (const char *name, int *r_algo, } return NULL; } + + +/* Map an OpenPGP public key algorithm number to the one used by + * Libgcrypt. Returns 0 for unknown gcry algorithm. */ +enum gcry_pk_algos +map_openpgp_pk_to_gcry (pubkey_algo_t algo) +{ + switch (algo) + { + case PUBKEY_ALGO_EDDSA: return GCRY_PK_EDDSA; + case PUBKEY_ALGO_ECDSA: return GCRY_PK_ECDSA; + case PUBKEY_ALGO_ECDH: return GCRY_PK_ECDH; + default: return algo < 110 ? (enum gcry_pk_algos)algo : 0; + } +} + + +/* Return a string describing the public key algorithm and the + * keysize. For elliptic curves the function prints the name of the + * curve because the keysize is a property of the curve. ALGO is the + * Gcrypt algorithmj number, curve is either NULL or give the PID of + * the curve, NBITS is either 0 or the size of the algorithms for RSA + * etc. The returned string is taken from permanent table. Examples + * for the output are: + * + * "rsa3072" - RSA with 3072 bit + * "elg1024" - Elgamal with 1024 bit + * "ed25519" - ECC using the curve Ed25519. + * "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". + * "E_1.3.6.1.4.1.11591.2.12242973" - ECC with a bogus OID. + * "unknown_N" - Unknown OpenPGP algorithm N. + * If N is > 110 this is a gcrypt algo. + */ +const char * +get_keyalgo_string (enum gcry_pk_algos algo, + unsigned int nbits, const char *curve) +{ + const char *prefix; + int i; + char *name, *curvebuf; + + switch (algo) + { + case GCRY_PK_RSA: prefix = "rsa"; break; + case GCRY_PK_ELG: prefix = "elg"; break; + case GCRY_PK_DSA: prefix = "dsa"; break; + case GCRY_PK_ECC: + case GCRY_PK_ECDH: + case GCRY_PK_ECDSA: + case GCRY_PK_EDDSA: prefix = ""; break; + default: prefix = NULL; break; + } + + if (prefix && *prefix && nbits) + { + for (i=0; i < keyalgo_strings_used; i++) + { + if (keyalgo_strings[i].algo == algo + && keyalgo_strings[i].nbits + && keyalgo_strings[i].nbits == nbits) + return keyalgo_strings[i].name; + } + /* Not yet in the table - add it. */ + name = xasprintf ("%s%u", prefix, nbits); + nbits = nbits? nbits : 1; /* No nbits - oops - use 1 instead. */ + curvebuf = NULL; + } + else if (prefix && !*prefix) + { + const char *curvename; + + for (i=0; i < keyalgo_strings_used; i++) + { + if (keyalgo_strings[i].algo == algo + && keyalgo_strings[i].curve && curve + && !strcmp (keyalgo_strings[i].curve, curve)) + return keyalgo_strings[i].name; + } + + /* Not yet in the table - add it. */ + curvename = openpgp_oid_or_name_to_curve (curve, 0); + if (curvename) + name = xasprintf ("%s", curvename); + else if (curve) + name = xasprintf ("E_%s", curve); + else + name = xasprintf ("E_error"); + nbits = 0; + curvebuf = curve? xstrdup (curve) : NULL; + } + else + { + for (i=0; i < keyalgo_strings_used; i++) + { + if (keyalgo_strings[i].algo == algo + && !keyalgo_strings[i].nbits + && !keyalgo_strings[i].curve) + return keyalgo_strings[i].name; + } + /* Not yet in the table - add it. */ + name = xasprintf ("unknown_%u", (unsigned int)algo); + nbits = 0; + curvebuf = NULL; + } + + /* Store a new entry. This is a loop because of a possible nPth + * thread switch during xrealloc. */ + while (keyalgo_strings_used >= keyalgo_strings_size) + { + keyalgo_strings_size += 10; + if (keyalgo_strings_size > 1024*1024) + log_fatal ("%s: table getting too large - possible DoS\n", __func__); + keyalgo_strings = xrealloc (keyalgo_strings, (keyalgo_strings_size + * sizeof *keyalgo_strings)); + } + keyalgo_strings[keyalgo_strings_used].algo = algo; + keyalgo_strings[keyalgo_strings_used].nbits = nbits; + keyalgo_strings[keyalgo_strings_used].curve = curvebuf; + keyalgo_strings[keyalgo_strings_used].name = name; + keyalgo_strings_used++; + + return name; /* Note that this is in the table. */ +} diff --git a/common/openpgpdefs.h b/common/openpgpdefs.h index 05f3621..e50c94e 100644 --- a/common/openpgpdefs.h +++ b/common/openpgpdefs.h @@ -130,8 +130,8 @@ sigsubpkttype_t; typedef enum { AEAD_ALGO_NONE = 0, - AEAD_ALGO_EAX = 1, - AEAD_ALGO_OCB = 2 + AEAD_ALGO_EAX = 1, /* Deprecated. */ + AEAD_ALGO_OCB = 2 /* The one and only. */ } aead_algo_t; @@ -226,5 +226,8 @@ gpg_error_t compute_openpgp_fpr_ecc (int keyversion, unsigned char *result, unsigned int *r_resultlen); +/*-- openpgp-oid.c --*/ +enum gcry_pk_algos map_openpgp_pk_to_gcry (pubkey_algo_t algo); + #endif /*GNUPG_COMMON_OPENPGPDEFS_H*/ diff --git a/common/recsel.c b/common/recsel.c index df77b57..3fb401f 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 * @@ -553,7 +522,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/sexputil.c b/common/sexputil.c index 68388e1..b97e174 100644 --- a/common/sexputil.c +++ b/common/sexputil.c @@ -536,7 +536,8 @@ get_rsa_pk_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; - if (!tok || toklen != 10 || memcmp ("public-key", tok, toklen)) + if (!tok || !((toklen == 10 && !memcmp ("public-key", tok, toklen)) + || (toklen == 11 && !memcmp ("private-key", tok, toklen)))) return gpg_error (GPG_ERR_BAD_PUBKEY); if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; @@ -613,7 +614,7 @@ get_ecc_q_from_canon_sexp (const unsigned char *keydata, size_t keydatalen, size_t buflen, toklen; int depth, last_depth1, last_depth2; const unsigned char *ecc_q = NULL; - size_t ecc_q_len; + size_t ecc_q_len = 0; *r_q = NULL; *r_qlen = 0; @@ -1068,6 +1069,8 @@ pubkey_algo_string (gcry_sexp_t s_pkey, enum gcry_pk_algos *r_algoid) l1 = gcry_sexp_find_token (s_pkey, "public-key", 0); if (!l1) + l1 = gcry_sexp_find_token (s_pkey, "private-key", 0); + if (!l1) return xtrystrdup ("E_no_key"); { gcry_sexp_t l_tmp = gcry_sexp_cadr (l1); diff --git a/common/signal.c b/common/signal.c index 92925fd..0200186 100644 --- a/common/signal.c +++ b/common/signal.c @@ -51,7 +51,7 @@ static void (*cleanup_fnc)(void); #ifndef HAVE_DOSISH_SYSTEM static void -init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) +init_one_signal (int sig, void (*handler)(int), int check_ign ) { # ifdef HAVE_SIGACTION struct sigaction oact, nact; @@ -69,7 +69,7 @@ init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign ) nact.sa_flags = 0; sigaction ( sig, &nact, NULL); # else - RETSIGTYPE (*ohandler)(int); + void (*ohandler)(int); ohandler = signal (sig, handler); if (check_ign && ohandler == SIG_IGN) @@ -98,7 +98,7 @@ get_signal_name( int signum ) #endif /*!HAVE_DOSISH_SYSTEM*/ #ifndef HAVE_DOSISH_SYSTEM -static RETSIGTYPE +static void got_fatal_signal (int sig) { const char *s; @@ -157,7 +157,7 @@ got_fatal_signal (int sig) #endif /*!HAVE_DOSISH_SYSTEM*/ #ifndef HAVE_DOSISH_SYSTEM -static RETSIGTYPE +static void got_usr_signal (int sig) { (void)sig; diff --git a/common/stringhelp.c b/common/stringhelp.c index 5baaa1b..f182a28 100644 --- a/common/stringhelp.c +++ b/common/stringhelp.c @@ -160,6 +160,36 @@ ascii_memistr ( const void *buffer, size_t buflen, const char *sub ) return NULL; } + +/* 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 @@ -694,7 +724,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) { @@ -1691,10 +1721,16 @@ format_text (const char *text_in, int target_cols, int max_cols) } -/* Substitute environment variables in STRING and return a new string. - * On error the function returns NULL. */ +/* Substitute variables in STRING and return a new string. GETVAL is + * a function which maps NAME to its value; that value is a string + * which may not change during the execution time of this function. + * If GETVAL returns NULL substitute_vars returns NULL and the caller + * may inspect ERRNO for the reason. In all other error cases this + * function also returns NULL. Caller must free the returned string. */ char * -substitute_envvars (const char *string) +substitute_vars (const char *string, + const char *(*getval)(void *cookie, const char *name), + void *cookie) { char *line, *p, *pend; const char *value; @@ -1745,19 +1781,22 @@ substitute_envvars (const char *string) { int save = *pend; *pend = 0; - value = getenv (p+2); + value = getval (cookie, p+2); *pend++ = save; } else { int save = *pend; *pend = 0; - value = getenv (p+1); + value = getval (cookie, p+1); *pend = save; } if (!value) - value = ""; + { + xfree (result); + return NULL; + } valuelen = strlen (value); if (valuelen <= pend - p) { @@ -1793,3 +1832,26 @@ substitute_envvars (const char *string) leave: return result; } + + +/* Helper for substitute_envvars. */ +static const char * +subst_getenv (void *cookie, const char *name) +{ + const char *s; + + (void)cookie; + + s = getenv (name); + return s? s : ""; +} + + +/* Substitute environment variables in STRING and return a new string. + * On error the function returns NULL. */ +char * +substitute_envvars (const char *string) +{ + return substitute_vars (string, subst_getenv, NULL); + +} diff --git a/common/stringhelp.h b/common/stringhelp.h index 84215c7..db73302 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); @@ -169,7 +170,10 @@ int compare_version_strings (const char *my_version, const char *req_version); /* Format a string so that it fits within about TARGET_COLS columns. */ char *format_text (const char *text, int target_cols, int max_cols); -/* Substitute environmen variabales in STRING. */ +/* Substitute variables in STRING. */ +char *substitute_vars (const char *string, + const char *(*getval)(void *cookie, const char *name), + void *cookie); char *substitute_envvars (const char *string); diff --git a/common/sysutils.c b/common/sysutils.c index 5f54ae1..0cd5f49 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -244,6 +244,9 @@ map_w32_to_errno (DWORD w32_err) case ERROR_ALREADY_EXISTS: return EEXIST; + case ERROR_FILE_INVALID: + return EIO; + /* This mapping has been taken from reactOS. */ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; case ERROR_ARENA_TRASHED: return ENOMEM; @@ -316,15 +319,17 @@ map_w32_to_errno (DWORD w32_err) #endif /*HAVE_W32_SYSTEM*/ -/* Set ERRNO from the Windows error. EC may be -1 to use the last error. */ +/* Set ERRNO from the Windows error. EC may be -1 to use the last + * error. Returns the Windows error code. */ #ifdef HAVE_W32_SYSTEM -void +int gnupg_w32_set_errno (int ec) { /* FIXME: Replace by gpgrt_w32_set_errno. */ if (ec == -1) ec = GetLastError (); _set_errno (map_w32_to_errno (ec)); + return ec; } #endif /*HAVE_W32_SYSTEM*/ @@ -809,7 +814,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/sysutils.h b/common/sysutils.h index e22156b..d33e9c1 100644 --- a/common/sysutils.h +++ b/common/sysutils.h @@ -107,7 +107,7 @@ int gnupg_inotify_has_name (int fd, const char *name); #ifdef HAVE_W32_SYSTEM -void gnupg_w32_set_errno (int ec); +int gnupg_w32_set_errno (int ec); void *w32_get_user_sid (void); #include "../common/w32help.h" diff --git a/common/t-b64.c b/common/t-b64.c index 3b63872..16c079d 100644 --- a/common/t-b64.c +++ b/common/t-b64.c @@ -1,30 +1,24 @@ /* t-b64.c - Module tests for b64enc.c and b64dec.c - * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008 Free Software Foundation, Inc. + * Copyright (C) 2008, 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,10 +30,112 @@ __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; + +/* 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) +{ + const char *s; + unsigned char *buffer; + size_t length; + + buffer = xmalloc (strlen(string)/2+1); + length = 0; + for (s=string; *s; s +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + return NULL; /* Invalid hex digits. */ + ((unsigned char*)buffer)[length++] = xtoi_2 (s); + } + *r_length = length; + return buffer; +} + + +static void +test_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; + void *data = NULL; + size_t datalen; + char *wantdata = NULL; + size_t wantdatalen; + + for (tidx = 0; tidx < DIM(tests); tidx++) + { + 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 (); + } + xfree (wantdata); + xfree (data); +} + + static void test_b64enc_pgp (const char *string) { @@ -101,6 +197,7 @@ test_b64enc_file (const char *fname) pass (); } + static void test_b64dec_file (const char *fname) { @@ -150,6 +247,7 @@ main (int argc, char **argv) { int do_encode = 0; int do_decode = 0; + int do_pgpdecode = 0; if (argc) { argc--; argv++; } @@ -169,13 +267,17 @@ main (int argc, char **argv) do_decode = 1; argc--; argv++; } + else if (argc) + do_pgpdecode = 1; if (do_encode) test_b64enc_file (argc? *argv: NULL); else if (do_decode) test_b64dec_file (argc? *argv: NULL); - else + else if (do_pgpdecode) test_b64enc_pgp (argc? *argv: NULL); + else + test_b64decode (); return !!errcount; } diff --git a/common/t-gettime.c b/common/t-gettime.c index 13cb1a2..76c3052 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/t-openpgp-oid.c b/common/t-openpgp-oid.c index fd9de5d..56fb6fe 100644 --- a/common/t-openpgp-oid.c +++ b/common/t-openpgp-oid.c @@ -27,7 +27,7 @@ #define pass() do { ; } while(0) #define fail(a,e) \ do { fprintf (stderr, "%s:%d: test %d failed (%s)\n", \ - __FILE__,__LINE__, (a), gpg_strerror (e)); \ + __func__, __LINE__, (a), gpg_strerror (e)); \ exit (1); \ } while(0) @@ -150,7 +150,7 @@ test_openpgp_oid_to_str (void) if (strcmp (string, samples[idx].string)) fail (idx, 0); xfree (string); -} + } } @@ -226,6 +226,74 @@ test_openpgp_enum_curves (void) } +static void +test_get_keyalgo_string (void) +{ + static struct + { + int algo; + unsigned int nbits; + const char *curve; + const char *name; + } samples[] = + { + { GCRY_PK_RSA, 1024, NULL, "rsa1024" }, + { GCRY_PK_RSA, 1536, NULL, "rsa1536" }, + { GCRY_PK_RSA, 768, NULL, "rsa768" }, + { GCRY_PK_DSA, 3072, NULL, "dsa3072" }, + { GCRY_PK_DSA, 1024, NULL, "dsa1024" }, + { GCRY_PK_ELG, 2048, NULL, "elg2048" }, + { GCRY_PK_ELG, 0, NULL, "unknown_20" }, + { 47114711, 1000, NULL, "unknown_47114711" }, + /* Note that we don't care about the actual ECC algorithm. */ + { GCRY_PK_EDDSA, 0, "1.3.6.1.4.1.11591.15.1", "ed25519" }, + { GCRY_PK_ECDSA, 0, "1.3.6.1.4.1.11591.15.1", "ed25519" }, + { GCRY_PK_ECDH, 0, "1.3.6.1.4.1.11591.15.1", "ed25519" }, + { GCRY_PK_ECDH, 0, "1.3.6.1.4.1.3029.1.5.1", "cv25519" }, + { GCRY_PK_ECDH, 0, "1.3.36.3.3.2.8.1.1.7", "brainpoolP256r1" }, + { GCRY_PK_ECDH, 0, "1.3.36.3.3.2.8.1.1.11", "brainpoolP384r1" }, + { GCRY_PK_ECDH, 0, "1.3.36.3.3.2.8.1.1.13", "brainpoolP512r1" }, + { GCRY_PK_ECDH, 0, "1.3.132.0.10", "secp256k1" }, + { GCRY_PK_ECDH, 0, "1.2.840.10045.3.1.7", "nistp256" }, + { GCRY_PK_ECDH, 0, "1.3.132.0.34", "nistp384" }, + { GCRY_PK_ECDH, 0, "1.3.132.0.35", "nistp521" }, + { GCRY_PK_ECDH, 0, "1.2.3.4.5.6", "E_1.2.3.4.5.6" }, + { GCRY_PK_ECDH, 0, BADOID, "E_1.3.6.1.4.1.11591.2.12242973" }, + + /* Some again to test existing lookups. */ + { GCRY_PK_RSA, 768, NULL, "rsa768" }, + { GCRY_PK_DSA, 3072, NULL, "dsa3072" }, + { GCRY_PK_DSA, 1024, NULL, "dsa1024" }, + { GCRY_PK_ECDH, 0, "1.3.6.1.4.1.11591.15.1", "ed25519" }, + { GCRY_PK_ECDH, 0, "1.3.6.1.4.1.3029.1.5.1", "cv25519" }, + { GCRY_PK_ECDH, 0, "1.3.36.3.3.2.8.1.1.7", "brainpoolP256r1" }, + { 47114711, 1000, NULL, "unknown_47114711" } + }; + int idx; + const char *name; + int oops = 0; + int pass; + + /* We do several passes becuase that is how the function is + * called. */ + for (pass=0; pass < 3; pass++) + for (idx=0; idx < DIM (samples); idx++) + { + name = get_keyalgo_string (samples[idx].algo, + samples[idx].nbits, + samples[idx].curve); + if (strcmp (samples[idx].name, name)) + { + fprintf (stderr, "%s:test %d.%d: want '%s' got '%s'\n", + __func__, pass, idx, samples[idx].name, name); + oops = 1; + } + } + if (oops) + exit (1); +} + + int main (int argc, char **argv) { @@ -241,6 +309,7 @@ main (int argc, char **argv) test_openpgp_oid_to_str (); test_openpgp_oid_is_ed25519 (); test_openpgp_enum_curves (); + test_get_keyalgo_string (); return 0; } diff --git a/common/t-session-env.c b/common/t-session-env.c index e2b942c..a65cb15 100644 --- a/common/t-session-env.c +++ b/common/t-session-env.c @@ -154,6 +154,7 @@ test_all (void) listall (se); +#ifndef HAVE_W32_SYSTEM /* Retrieve a default one. */ s = session_env_getenv_or_default (se, "HOME", NULL); if (!s) @@ -161,14 +162,17 @@ test_all (void) fprintf (stderr, "failed to get default of HOME\n"); exit (1); } +#endif s = session_env_getenv (se, "HOME"); if (s) fail(0); /* This is a default value, thus we should not see it. */ +#ifndef HAVE_W32_SYSTEM s = session_env_getenv_or_default (se, "HOME", NULL); if (!s) fail(0); /* But here we should see it. */ +#endif /* Add a few more. */ err = session_env_putenv (se, "X1=A value"); diff --git a/common/t-stringhelp.c b/common/t-stringhelp.c index d76991f..7d25cbd 100644 --- a/common/t-stringhelp.c +++ b/common/t-stringhelp.c @@ -89,7 +89,15 @@ mygetcwd (void) return buffer; #else if (getcwd (buffer, size) == buffer) - return buffer; + { +#ifdef HAVE_W32_SYSTEM + char *p; + for (p = buffer; *p; p++) + if (*p == '\\') + *p = '/'; +#endif + return buffer; + } xfree (buffer); if (errno != ERANGE) { diff --git a/common/tlv.c b/common/tlv.c index 9618d04..4ba9ef2 100644 --- a/common/tlv.c +++ b/common/tlv.c @@ -150,8 +150,10 @@ find_tlv_unchecked (const unsigned char *buffer, size_t length, /* 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. */ + * and the length part from the TLV triplet. Update BUFFER and SIZE + * on success. Note that this function does not check that the value + * fits into the provided buffer; this allows to work on the TL part + * of a TLV. */ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, int *r_class, int *r_tag, diff --git a/common/types.h b/common/types.h index 8e551df..28f3779 100644 --- a/common/types.h +++ b/common/types.h @@ -67,29 +67,29 @@ Windows typedefs byte in the RPC headers but we need to avoid a warning about a double definition. */ -#ifndef HAVE_BYTE_TYPEDEF +#ifndef HAVE_TYPE_BYTE # undef byte /* There might be a macro with this name. */ # ifdef __riscos__ typedef char byte; # elif !(defined(_WIN32) && defined(cbNDRContext)) typedef unsigned char byte; # endif -# define HAVE_BYTE_TYPEDEF -#endif /*!HAVE_BYTE_TYPEDEF*/ +# define HAVE_TYPE_BYTE +#endif /*!HAVE_TYPE_BYTE*/ -#ifndef HAVE_USHORT_TYPEDEF +#ifndef HAVE_TYPE_USHORT # undef ushort /* There might be a macro with this name. */ typedef unsigned short ushort; -# define HAVE_USHORT_TYPEDEF +# define HAVE_TYPE_USHORT #endif -#ifndef HAVE_ULONG_TYPEDEF +#ifndef HAVE_TYPE_ULONG # undef ulong /* There might be a macro with this name. */ typedef unsigned long ulong; -# define HAVE_ULONG_TYPEDEF +# define HAVE_TYPE_ULONG #endif -#ifndef HAVE_U16_TYPEDEF +#ifndef HAVE_TYPE_U16 # undef u16 /* There might be a macro with this name. */ # if SIZEOF_UNSIGNED_INT == 2 typedef unsigned int u16; @@ -98,10 +98,10 @@ # else # error no typedef for u16 # endif -# define HAVE_U16_TYPEDEF +# define HAVE_TYPE_U16 #endif -#ifndef HAVE_U32_TYPEDEF +#ifndef HAVE_TYPE_U32 # undef u32 /* There might be a macro with this name. */ # if SIZEOF_UNSIGNED_INT == 4 typedef unsigned int u32; @@ -110,7 +110,7 @@ # else # error no typedef for u32 # endif -# define HAVE_U32_TYPEDEF +# define HAVE_TYPE_U32 #endif #endif /*GNUPG_COMMON_TYPES_H*/ diff --git a/common/util.h b/common/util.h index 82b3a34..ca38122 100644 --- a/common/util.h +++ b/common/util.h @@ -39,10 +39,6 @@ * libgpg-error version. Define them here. * Example: (#if GPG_ERROR_VERSION_NUMBER < 0x011500 // 1.21) */ -#if GPG_ERROR_VERSION_NUMBER < 0x012400 /* 1.36 */ -# define GPG_ERR_NO_AUTH 314 -# define GPG_ERR_BAD_AUTH 315 -#endif #ifndef EXTERN_UNLESS_MAIN_MODULE # if !defined (INCLUDED_BY_MAIN_MODULE) @@ -169,6 +165,8 @@ gpg_error_t b64dec_start (struct b64state *state, const char *title); gpg_error_t b64dec_proc (struct b64state *state, void *buffer, size_t length, size_t *r_nbytes); gpg_error_t b64dec_finish (struct b64state *state); +gpg_error_t b64decode (const char *string, const char *title, + void **r_buffer, size_t *r_buflen); /*-- sexputil.c */ char *canon_sexp_to_string (const unsigned char *canon, size_t canonlen); @@ -248,9 +246,12 @@ int openpgp_oid_is_cv25519 (gcry_mpi_t a); const char *openpgp_curve_to_oid (const char *name, unsigned int *r_nbits, int *r_algo); const char *openpgp_oid_to_curve (const char *oid, int canon); +const char *openpgp_oid_or_name_to_curve (const char *oidname, int canon); const char *openpgp_enum_curves (int *idxp); const char *openpgp_is_curve_supported (const char *name, int *r_algo, unsigned int *r_nbits); +const char *get_keyalgo_string (enum gcry_pk_algos algo, + unsigned int nbits, const char *curve); /*-- homedir.c --*/ @@ -344,7 +345,7 @@ char *try_make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim); char *decode_c_string (const char *src); -int is_file_compressed (const char *s, int *ret_rc); +int is_file_compressed (const byte *buf, unsigned int buflen); int match_multistr (const char *multistr,const char *match); diff --git a/common/w32info-rc.h.in b/common/w32info-rc.h.in index 4c0e034..1e76b58 100644 --- a/common/w32info-rc.h.in +++ b/common/w32info-rc.h.in @@ -29,4 +29,4 @@ built on @BUILD_HOSTNAME@ at @BUILD_TIMESTAMP@\0" #define W32INFO_PRODUCTVERSION "@VERSION@\0" #define W32INFO_LEGALCOPYRIGHT "Copyright \xa9 \ -2022 g10 Code GmbH\0" +2023 g10 Code GmbH\0" |