From 79ba2dd9899da86e05edaf79d4cec5f14e650cca Mon Sep 17 00:00:00 2001 From: Aleksey Sanin Date: Tue, 29 Sep 2026 21:38:00 -0400 Subject: [PATCH] Fixed minor unit tests and build system issues --- Makefile.am | 7 + apps/cmdline.c | 121 ++++++++++++++---- apps/oss-fuzz/standalone_fuzz_runner.c | 9 ++ apps/oss-fuzz/xmlsec_dsig_verify_target.c | 10 ++ apps/oss-fuzz/xmlsec_keyinfo_target.c | 1 + apps/oss-fuzz/xmlsec_relationship_target.c | 16 ++- apps/unit_tests/base64_unit_tests.c | 21 ++- apps/unit_tests/buffer_unit_tests.c | 10 +- apps/unit_tests/list_unit_tests.c | 3 + .../unit_tests/transform_helpers_unit_tests.c | 3 +- apps/unit_tests/xmlsec_unit_tests.c | 4 +- apps/xmlsec.c | 32 ++++- configure.ac | 8 +- scripts/build_release.sh | 11 ++ scripts/build_windows.sh | 56 +++++--- scripts/gen-github-wiki.py | 32 ++++- src/gnutls/x509vfy.c | 2 +- src/mscng/ciphers_cbc.c | 2 +- 18 files changed, 279 insertions(+), 69 deletions(-) diff --git a/Makefile.am b/Makefile.am index 836160123..4804e58fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -104,6 +104,13 @@ if XMLSEC_APPS check: check-all check-fuzz check-info +# Build the command line tool and the unit tests on demand so the check-* / +# memcheck / perfcheck targets can be run directly in a fresh build dir, +# before a full 'make' has built the subdirectories. The whole project is +# built (not just apps/) so the library dependencies in src/ are available. +$(TEST_APP) $(UNIT_TEST_APP): + $(MAKE) $(AM_MAKEFLAGS) all + check-unit-tests: $(UNIT_TEST_APP) @($(PRECHECK_COMMANDS) && $(ABS_BUILDDIR)/$(UNIT_TEST_APP)) diff --git a/apps/cmdline.c b/apps/cmdline.c index bd810a0a9..1eab90a55 100644 --- a/apps/cmdline.c +++ b/apps/cmdline.c @@ -10,6 +10,7 @@ */ #include #include +#include #include #include #include @@ -36,10 +37,8 @@ static int xmlSecAppCmdLineTimeParamRead (const char* str int is_gmt_time); #if defined(_MSC_VER) -#define XMLSEC_SCANF sscanf_s #define XMLSEC_MKGMTIME _mkgmtime #else /* defined(_MSC_VER) */ -#define XMLSEC_SCANF sscanf #define XMLSEC_MKGMTIME xmlSecAppGetGmtTime static time_t xmlSecAppGetGmtTime (struct tm* timeptr); @@ -441,51 +440,123 @@ xmlSecAppIsValidDate(int year, int month, int day) { return((day >= 1) && (day <= maxDay)); } +/** + * Reads 1 to maxDigits digits from *pp, advancing *pp past them. + * Returns the numeric value, or -1 if no digit is present. + */ +static int +xmlSecAppCmdLineParseDigits(const char** pp, int maxDigits) { + int value = 0; + int count = 0; + const char* p = *pp; + + while((count < maxDigits) && (*p >= '0') && (*p <= '9')) { + value = (value * 10) + (*p - '0'); + p++; + count++; + } + if(count == 0) { + return(-1); + } + *pp = p; + return(value); +} + static int xmlSecAppCmdLineTimeParamRead(const char* str, time_t* t, int is_gmt_time) { struct tm tm; - int n; - int consumed = 0; - const char* rest; + int year, month, day, hour, minute, second; + const char* p; if((str == NULL) || (t == NULL)) { return(-1); } memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; - n = XMLSEC_SCANF(str, "%4d-%2d-%2d%*c%2d:%2d:%2d%n", - &tm.tm_year, &tm.tm_mon, &tm.tm_mday, - &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &consumed); - if(n != 6) { + + /* Parse the documented "YYYY-MM-DD HH:MM:SS" format. The date and time + * parts are separated by a single character (any character, e.g. a space, + * 'T', or '+'), followed by optional whitespace. Parsing is done manually + * (rather than with sscanf("%n")) so that it behaves identically on MSVC, + * where sscanf_s does not support the %n specifier. */ + p = str; + + year = xmlSecAppCmdLineParseDigits(&p, 4); + if((year < 0) || (*p != '-')) { return(-1); } + p++; - /* reject trailing garbage (trailing whitespace is allowed) */ - rest = str + consumed; - while(*rest != '\0') { - if((*rest != ' ') && (*rest != '\t')) { - return(-1); - } - ++rest; + month = xmlSecAppCmdLineParseDigits(&p, 2); + if((month < 0) || (*p != '-')) { + return(-1); + } + p++; + + day = xmlSecAppCmdLineParseDigits(&p, 2); + if(day < 0) { + return(-1); + } + + /* Skip exactly one separator character between the date and the time, + * matching the original sscanf "%*c" (which accepts any single character, + * e.g. a space, 'T', or '+'), then any additional leading whitespace. */ + if(*p == '\0') { + return(-1); + } + p++; + while(isspace((unsigned char)*p)) { + p++; + } + + hour = xmlSecAppCmdLineParseDigits(&p, 2); + if((hour < 0) || (*p != ':')) { + return(-1); + } + p++; + + minute = xmlSecAppCmdLineParseDigits(&p, 2); + if((minute < 0) || (*p != ':')) { + return(-1); + } + p++; + + second = xmlSecAppCmdLineParseDigits(&p, 2); + if(second < 0) { + return(-1); + } + + /* reject trailing garbage (trailing whitespaces are allowed) */ + while(isspace((unsigned char)*p)) { + p++; + } + if((*p) != '\0') { + return(-1); } - if((tm.tm_year < 1900) - || (tm.tm_mon < 1) || (tm.tm_mon > 12) - || (tm.tm_mday < 1) || (tm.tm_mday > 31) - || (tm.tm_hour < 0) || (tm.tm_hour > 23) - || (tm.tm_min < 0) || (tm.tm_min > 59) - || (tm.tm_sec < 0) || (tm.tm_sec > 61)) { + /* check values for basic validity */ + if((year < 1900) + || (month < 1) || (month > 12) + || (day < 1) || (day > 31) + || (hour < 0) || (hour > 23) + || (minute < 0) || (minute > 59) + || (second < 0) || (second > 61) + ) { return(-1); } /* reject invalid calendar dates (e.g. "2020-02-30") that the field * range checks above would otherwise let mktime() silently normalize */ - if(xmlSecAppIsValidDate(tm.tm_year, tm.tm_mon, tm.tm_mday) == 0) { + if(xmlSecAppIsValidDate(year, month, day) == 0) { return(-1); } - tm.tm_year -= 1900; /* tm relative format year */ - tm.tm_mon -= 1; /* tm relative format month */ + tm.tm_year = year - 1900; /* tm relative format year */ + tm.tm_mon = month - 1; /* tm relative format month */ + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = minute; + tm.tm_sec = second; if(is_gmt_time != 0) { (*t) = XMLSEC_MKGMTIME(&tm); diff --git a/apps/oss-fuzz/standalone_fuzz_runner.c b/apps/oss-fuzz/standalone_fuzz_runner.c index 39c2c7479..3b3c43cff 100644 --- a/apps/oss-fuzz/standalone_fuzz_runner.c +++ b/apps/oss-fuzz/standalone_fuzz_runner.c @@ -117,6 +117,15 @@ int main(int argc, const char **argv) { fuzzer_close_file(f); continue; } + /* On 32-bit builds the file offset is 64-bit but size_t is 32-bit; + * reject files that would not fit in size_t to avoid silent truncation + * of the input size. The sizeof() guard keeps the SIZE_MAX cast valid + * (it is only evaluated when the offset type is wider than size_t). */ + if (sizeof(fuzzer_file_offset_t) > sizeof(size_t) && len > (fuzzer_file_offset_t)SIZE_MAX) { + FUZZER_ERROR("file is too large", argv[i]); + fuzzer_close_file(f); + continue; + } rewind(f); /* Allocate at least one byte so that zero-length inputs still get a diff --git a/apps/oss-fuzz/xmlsec_dsig_verify_target.c b/apps/oss-fuzz/xmlsec_dsig_verify_target.c index d57f04a3a..4cc24f259 100644 --- a/apps/oss-fuzz/xmlsec_dsig_verify_target.c +++ b/apps/oss-fuzz/xmlsec_dsig_verify_target.c @@ -80,12 +80,16 @@ static int do_init(void) { return -1; } if (xmlSecCheckVersion() != 1) { + xmlSecShutdown(); return -1; } if (xmlSecOpenSSLAppInit(NULL) < 0) { + xmlSecShutdown(); return -1; } if (xmlSecOpenSSLInit() < 0) { + xmlSecOpenSSLAppShutdown(); + xmlSecShutdown(); return -1; } @@ -96,11 +100,17 @@ static int do_init(void) { /* Build a keys manager once and load the fixed trusted cert into it. */ g_mngr = xmlSecKeysMngrCreate(); if (g_mngr == NULL) { + xmlSecOpenSSLShutdown(); + xmlSecOpenSSLAppShutdown(); + xmlSecShutdown(); return -1; } if (xmlSecOpenSSLAppDefaultKeysMngrInit(g_mngr) < 0) { xmlSecKeysMngrDestroy(g_mngr); g_mngr = NULL; + xmlSecOpenSSLShutdown(); + xmlSecOpenSSLAppShutdown(); + xmlSecShutdown(); return -1; } /* Re-assemble the PEM cert (with real newline characters) into a buffer. */ diff --git a/apps/oss-fuzz/xmlsec_keyinfo_target.c b/apps/oss-fuzz/xmlsec_keyinfo_target.c index c89270c81..43184bca3 100644 --- a/apps/oss-fuzz/xmlsec_keyinfo_target.c +++ b/apps/oss-fuzz/xmlsec_keyinfo_target.c @@ -74,6 +74,7 @@ static int do_init(void) { return -1; } if (xmlSecOpenSSLInit() < 0) { + xmlSecOpenSSLAppShutdown(); xmlSecShutdown(); return -1; } diff --git a/apps/oss-fuzz/xmlsec_relationship_target.c b/apps/oss-fuzz/xmlsec_relationship_target.c index f16990df5..c244aa607 100644 --- a/apps/oss-fuzz/xmlsec_relationship_target.c +++ b/apps/oss-fuzz/xmlsec_relationship_target.c @@ -59,9 +59,19 @@ static int do_init(void) { return 0; } -static xmlNodePtr find_relationships(xmlNodePtr node) { +/* Maximum recursion depth for find_relationships(). libxml2 caps element + * nesting at xmlParserMaxDepth (256 in normal mode; this target does not use + * XML_PARSE_HUGE), so a valid input can never reach this bound. It exists as + * a defense-in-depth cap so the recursion cannot exhaust the stack even if + * the parser depth limit were raised or absent. */ +#define RELATIONSHIPS_MAX_DEPTH 10000 + +static xmlNodePtr find_relationships(xmlNodePtr node, int depth) { xmlNodePtr cur; + if (depth >= RELATIONSHIPS_MAX_DEPTH) { + return NULL; + } for (cur = node; cur != NULL; cur = cur->next) { if (cur->type == XML_ELEMENT_NODE && cur->ns != NULL && cur->ns->href != NULL && @@ -69,7 +79,7 @@ static xmlNodePtr find_relationships(xmlNodePtr node) { return cur; } if (cur->children != NULL) { - xmlNodePtr found = find_relationships(cur->children); + xmlNodePtr found = find_relationships(cur->children, depth + 1); if (found != NULL) { return found; } @@ -103,7 +113,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { } transform_node = xmlSecFindNode(root, xmlSecNodeTransform, xmlSecDSigNs); - relationships_node = find_relationships(root); + relationships_node = find_relationships(root, 0); if (transform_node == NULL || relationships_node == NULL) { goto done; } diff --git a/apps/unit_tests/base64_unit_tests.c b/apps/unit_tests/base64_unit_tests.c index f5ba71058..c54853c1f 100644 --- a/apps/unit_tests/base64_unit_tests.c +++ b/apps/unit_tests/base64_unit_tests.c @@ -98,17 +98,23 @@ test_base64_decode_exact_size( const char * expected, xmlSecSize expectedSize ) { - xmlSecByte decoded[256]; + xmlSecByte decoded[257]; xmlSecSize decodedSize = 0; + xmlSecSize i; int ret; xmlSecAssert(name != NULL); xmlSecAssert(str != NULL); xmlSecAssert(expected != NULL); - xmlSecAssert(expectedSize <= sizeof(decoded)); + /* Keep at least one canary byte past the declared buffer size. */ + xmlSecAssert(expectedSize < sizeof(decoded)); testStart(name); + /* Fill the decode target with 0xAA and the remainder with a canary (0xBB) + * so any write past the declared buffer size (expectedSize) is detected, + * even though the backing array is larger. */ + memset(decoded, 0xBB, sizeof(decoded)); memset(decoded, 0xAA, expectedSize); ret = xmlSecBase64Decode_ex(BAD_CAST str, decoded, expectedSize, &decodedSize); if(ret < 0) { @@ -118,6 +124,17 @@ test_base64_decode_exact_size( return; } + /* The function must not write past the declared buffer size; the canary + * bytes beyond expectedSize must remain untouched. */ + for (i = expectedSize; i < sizeof(decoded); i++) { + if (decoded[i] != 0xBB) { + testLog("Error: base64 decode wrote past the exactly-sized buffer (%d bytes) for '%s'\n", + (int)expectedSize, str); + testFinishedFailure(); + return; + } + } + if(decodedSize != expectedSize) { testLog("Error: base64 decode returned size=%d (expected: %d) for '%s'\n", (int)decodedSize, (int)expectedSize, str); diff --git a/apps/unit_tests/buffer_unit_tests.c b/apps/unit_tests/buffer_unit_tests.c index 88fd630eb..dee478102 100644 --- a/apps/unit_tests/buffer_unit_tests.c +++ b/apps/unit_tests/buffer_unit_tests.c @@ -1489,6 +1489,7 @@ static void test_buffer_read_file(void) { xmlSecBufferPtr buf = NULL; char tmpName[160] = { '\0' }; + char tmpNameMissing[160] = { '\0' }; const xmlSecByte payload[] = { 0x00, 0x01, 0x02, 0xAB, 0xCD, 0xEF, 0xFF }; FILE* f = NULL; int fileCreated = 0; @@ -1501,6 +1502,13 @@ test_buffer_read_file(void) { testFinishedFailure(); return; } + /* A name under the temp dir that is intentionally never created, for the + * missing-file negative test (avoids a CWD-relative hardcoded name). */ + if(test_buffer_make_temp_name(tmpNameMissing, sizeof(tmpNameMissing), "readfile_missing.bin") < 0) { + testLog("Error: failed to build missing-file temp name\n"); + testFinishedFailure(); + return; + } /* write a known payload to a temp file in the temp directory */ #ifndef _MSC_VER @@ -1544,7 +1552,7 @@ test_buffer_read_file(void) { } /* a missing file must be rejected */ - if(xmlSecBufferReadFile(buf, "xmlsec_unit_tests_no_such_file_xyz.bin") >= 0) { + if(xmlSecBufferReadFile(buf, tmpNameMissing) >= 0) { testLog("Error: xmlSecBufferReadFile should fail for a missing file\n"); goto done; } diff --git a/apps/unit_tests/list_unit_tests.c b/apps/unit_tests/list_unit_tests.c index 557dd78aa..9f1c037b1 100644 --- a/apps/unit_tests/list_unit_tests.c +++ b/apps/unit_tests/list_unit_tests.c @@ -1035,6 +1035,9 @@ test_ptr_list_invalid_alloc_mode(void) { ret = xmlSecPtrListAdd(list, item); if(ret >= 0) { testLog("Error: xmlSecPtrListAdd succeeded with an invalid allocation mode\n"); + /* The add succeeded, so the item is now owned by the list; set item to + * NULL so the done: path does not destroy it a second time. */ + item = NULL; goto done; } if(xmlSecPtrListGetSize(list) != 0) { diff --git a/apps/unit_tests/transform_helpers_unit_tests.c b/apps/unit_tests/transform_helpers_unit_tests.c index 1bfff3aa3..a7681c065 100644 --- a/apps/unit_tests/transform_helpers_unit_tests.c +++ b/apps/unit_tests/transform_helpers_unit_tests.c @@ -193,7 +193,8 @@ test_xmlSecTransformChaCha20ParamsWrite_roundtrip(void) { xmlFree(counterContent); ret = xmlSecTransformChaCha20ParamsRead(node, ivRoundTrip, sizeof(ivRoundTrip), &ivSize, &noncePresent); - if((ret < 0) || (ivSize != XMLSEC_CHACHA20_IV_SIZE) || (memcmp(ivRoundTrip, iv, sizeof(iv)) != 0)) { + if((ret < 0) || (ivSize != XMLSEC_CHACHA20_IV_SIZE) || (noncePresent != 1) || + (memcmp(ivRoundTrip, iv, sizeof(iv)) != 0)) { testLog("Error: ChaCha20 params write did not round-trip through strict read\n"); xmlFreeDoc(doc); testFinishedFailure(); diff --git a/apps/unit_tests/xmlsec_unit_tests.c b/apps/unit_tests/xmlsec_unit_tests.c index 6bbf69ad8..03d8c7a8a 100644 --- a/apps/unit_tests/xmlsec_unit_tests.c +++ b/apps/unit_tests/xmlsec_unit_tests.c @@ -334,7 +334,7 @@ void testStart(const char * name) { void testFinishedSuccess(void) { if(g_testGroupSkip) { return; } - fprintf(stdout, " %s OK\n", testsName); + fprintf(stdout, " %s OK\n", testsName != NULL ? testsName : "(no test name)"); testLogReset(); testsFinishedSuccess += 1; testsName = NULL; @@ -342,7 +342,7 @@ void testFinishedSuccess(void) { void testFinishedFailure(void) { if(g_testGroupSkip) { return; } - fprintf(stdout, " %s FAILED\n", testsName); + fprintf(stdout, " %s FAILED\n", testsName != NULL ? testsName : "(no test name)"); testLogFlush(); testsFinishedFailed += 1; testsName = NULL; diff --git a/apps/xmlsec.c b/apps/xmlsec.c index 7640f205a..d9add49c8 100644 --- a/apps/xmlsec.c +++ b/apps/xmlsec.c @@ -1450,6 +1450,14 @@ int main(int argc, const char **argv) { goto done; } break; + case xmlSecAppCommandCheckKeyData: + case xmlSecAppCommandCheckTransforms: + if(pos >= argc) { + fprintf(stderr, "Error: at least one parameter is required for this command\n"); + xmlSecAppPrintUsage(); + goto done; + } + break; default: break; } @@ -1725,7 +1733,7 @@ xmlSecAppExecute(xmlSecAppCommand command, const char** utf8_argv, int argc) { g_repeats = xmlSecAppCmdLineParamGetInt(&repeatParam, 1); msecs = (1000 * (long double)g_totalTime) / (long double)CLOCKS_PER_SEC; - fprintf(stderr, "Executed %d tests in %.2Lf msec\n", g_repeats, msecs); + fprintf(stderr, "Executed %d tests in %.2f msec\n", g_repeats, (double)msecs); } /* success! */ @@ -1961,6 +1969,10 @@ xmlSecAppPrepareDSigCtx(xmlSecDSigCtxPtr dsigCtx) { int minHmacOutLen = (int)xmlSecTransformHmacGetMinOutputBitsSize(); minHmacOutLen = xmlSecAppCmdLineParamGetInt(&hmacMinOutputLenParam, minHmacOutLen); + if(minHmacOutLen < 0) { + fprintf(stderr, "Error: hmac min output length should be greater than or equal to zero\n"); + return(-1); + } xmlSecTransformHmacSetMinOutputBitsSize((xmlSecSize)minHmacOutLen); } #endif /* XMLSEC_NO_HMAC */ @@ -2020,7 +2032,10 @@ xmlSecAppEncryptFile(const char* inputFileName, const char* outputFileNameTmpl) goto done; } - /* parse doc and find template node */ + /* parse the template and find the node to fill. The + * --node-id/--node-name/--node-xpath options select the node in the + * --xml-data file (handled below), not the template node, so the template + * always uses the default node. */ doc = xmlSecParseFile(inputFileName); if(doc == NULL) { fprintf(stderr, "Error: failed to parse xml file \"%s\"\n", inputFileName); @@ -2270,7 +2285,12 @@ xmlSecAppPrepareKeyInfoCtx(xmlSecKeyInfoCtxPtr keyInfoCtx) { keyInfoCtx->flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_SKIP_TIME_CHECKS; } if(xmlSecAppCmdLineParamIsSet(&depthParam)) { - keyInfoCtx->certsVerificationDepth = xmlSecAppCmdLineParamGetInt(&depthParam, 0); + int depth = xmlSecAppCmdLineParamGetInt(&depthParam, 0); + if(depth < 0) { + fprintf(stderr, "Error: certificate verification depth should be greater than or equal to zero\n"); + return(-1); + } + keyInfoCtx->certsVerificationDepth = depth; } if(xmlSecAppCmdLineParamIsSet(&X509SkipStrictChecksParam)) { keyInfoCtx->flags |= XMLSEC_KEYINFO_FLAGS_X509DATA_SKIP_STRICT_CHECKS; @@ -3876,7 +3896,11 @@ xmlSecAppAddIDAttrCallback(xmlNodePtr cur, void* data) { /* check that we don't have same ID already */ tmpAttr = xmlGetID(cur->doc, id); if(tmpAttr == NULL) { - xmlAddID(NULL, cur->doc, id, attr); + if(xmlAddID(NULL, cur->doc, id, attr) == NULL) { + fprintf(stderr, "Error: failed to register ID attribute \"%s\"\n", id); + xmlFree(id); + return(-1); + } } else if(tmpAttr != attr) { fprintf(stderr, "Error: duplicate ID attribute \"%s\"\n", id); xmlFree(id); diff --git a/configure.ac b/configure.ac index 429cc8b70..49066132b 100644 --- a/configure.ac +++ b/configure.ac @@ -862,7 +862,7 @@ if test "z$OPENSSL_FOUND" = "zyes" ; then if test "z$XMLSEC_OPENSSL_VERSION" = "z" ; then AC_EGREP_CPP(greater-than-minvers, [ #include - #if OPENSSL_VERSION_NUMBER >= 0x40000000L + #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x40000000L greater-than-minvers #endif ],[ @@ -876,7 +876,7 @@ if test "z$OPENSSL_FOUND" = "zyes" ; then if test "z$XMLSEC_OPENSSL_VERSION" = "z" ; then AC_EGREP_CPP(greater-than-minvers, [ #include - #if OPENSSL_VERSION_NUMBER >= 0x30000000L + #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L greater-than-minvers #endif ],[ @@ -890,7 +890,7 @@ if test "z$OPENSSL_FOUND" = "zyes" ; then if test "z$XMLSEC_OPENSSL_VERSION" = "z" ; then AC_EGREP_CPP(greater-than-minvers, [ #include - #if OPENSSL_VERSION_NUMBER >= 0x10101000L + #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10101000L greater-than-minvers #endif ],[ @@ -1284,7 +1284,7 @@ if test "z$GNUTLS_FOUND" = "zno" -a "z$with_gnutls" != "z" -a "z$with_gnutls" != GNUTLS_FOUND="yes" AC_MSG_RESULT([yes]) else - AC_MSG_ERROR([not found: "$with_gnutls/bin/$GNUTLS_CONFIG" tool, or "$with_gnutls/include/$GNUTLS_INCLUDE_MARKER" and/or "$with_gnutls/lib/$GNUTLS_LIB_MARKER"]) + AC_MSG_ERROR([not found: "$with_gnutls/include/$GNUTLS_INCLUDE_MARKER" and/or "$with_gnutls/lib/$GNUTLS_LIB_MARKER"]) fi fi diff --git a/scripts/build_release.sh b/scripts/build_release.sh index db291d88f..063b52e9f 100755 --- a/scripts/build_release.sh +++ b/scripts/build_release.sh @@ -39,6 +39,9 @@ cd "$build_root" echo "============== Checking out the module '$git_uri'" git clone "$git_uri" cd xmlsec +# Remember the commit the tarball will be built from, so we can later verify +# that the local repository (where the release tags are created) matches it. +clone_commit=$(git rev-parse HEAD) find . -name .git -exec rm -rf {} + ac_version=`awk -F'[][]' '/^AC_INIT/ {print $4; exit}' configure.ac` @@ -62,6 +65,14 @@ echo "============== Signing tar file" gpg --output "${sig_file}" --detach-sig "${tar_file}" echo "============== Creating local tags for release ${full_version}" +# Verify the local repository (where the tags are created) is at the same +# commit as the clone that was actually built; otherwise the tag would point +# to a different source than the packaged tarball. +local_commit=$(git rev-parse HEAD) +if [ "$clone_commit" != "$local_commit" ]; then + echo "ERROR: local repository commit ($local_commit) does not match the built clone ($clone_commit); refusing to tag" + exit 1 +fi git tag -a "${full_version}" -f -m "XMLSec release ${full_version}" git tag -a "xmlsec_${git_version_tag}" -f -m "XMLSec release ${full_version}" echo "RUN MANUALLY: git push --follow-tags" diff --git a/scripts/build_windows.sh b/scripts/build_windows.sh index caa4b3879..fdb5df741 100755 --- a/scripts/build_windows.sh +++ b/scripts/build_windows.sh @@ -57,10 +57,20 @@ else fi # things that depend on the release vs debug build -libxml2_install_dir="${top_install_dir}\libxml2" -libxslt_install_dir="${top_install_dir}\libxslt" -openssl_install_dir="${top_install_dir}\openssl" -xmlsec_install_dir="${top_install_dir}\xmlsec" +libxml2_install_dir="${top_install_dir}/libxml2" +libxslt_install_dir="${top_install_dir}/libxslt" +openssl_install_dir="${top_install_dir}/openssl" +xmlsec_install_dir="${top_install_dir}/xmlsec" + +# Windows-style (mixed, e.g. "D:\...") paths for the native tools (cmake/perl/ +# nmake/powershell). Native tools interpret a leading "/cygdrive/..." as a +# drive-relative path, so the POSIX paths above must be converted with cygpath +# before being passed to them. +libxml2_install_dir_win=$(cygpath -m "${libxml2_install_dir}") +libxslt_install_dir_win=$(cygpath -m "${libxslt_install_dir}") +openssl_install_dir_win=$(cygpath -m "${openssl_install_dir}") +xmlsec_install_dir_win=$(cygpath -m "${xmlsec_install_dir}") +top_install_dir_win=$(cygpath -m "${top_install_dir}") zip_folders_and_files="libxml2 libxslt openssl xmlsec README.md" zip_output_file="${top_install_dir}\xmlsec1-${xmlsec_version}-win64${ZIP_POSTFIX}.zip" @@ -79,7 +89,7 @@ function build_libxml2 { fi # Build it. - cd "${work_dir}" + cd "${work_dir}" || return 1 rm -rf "${work_dir}\\${full_name}" "${libxml2_install_dir}" if [ ! -f "${full_name}.tar.gz" ] ; then @@ -97,8 +107,8 @@ function build_libxml2 { cmake -B "${LIBXML2_LIBXSLT_CMAKE_BUILDDIR}" -A "${LIBXML2_LIBXSLT_CMAKE_ARCH}" -G "${LIBXML2_LIBXSLT_CMAKE_GENERATOR}" \ -D CMAKE_MSVC_RUNTIME_LIBRARY="${LIBXML2_LIBXSLT_CMAKE_RUNTIME}" \ -D BUILD_SHARED_LIBS="${LIBXML2_LIBXSLT_CMAKE_SHARED_LIBS}" \ - -D CMAKE_PREFIX_PATH="${top_install_dir}" \ - -D CMAKE_INSTALL_PREFIX="${libxml2_install_dir}" \ + -D CMAKE_PREFIX_PATH="${top_install_dir_win}" \ + -D CMAKE_INSTALL_PREFIX="${libxml2_install_dir_win}" \ -D LIBXML2_WITH_ICONV=OFF \ -D LIBXML2_WITH_PYTHON=OFF \ -D LIBXML2_WITH_ZLIB=OFF \ @@ -137,7 +147,7 @@ function build_libxslt { fi # Build it. - cd "${work_dir}" + cd "${work_dir}" || return 1 rm -rf "${work_dir}\\${full_name}" "${libxslt_install_dir}" if [ ! -f "${full_name}.tar.gz" ] ; then @@ -156,8 +166,8 @@ function build_libxslt { cmake -B "${LIBXML2_LIBXSLT_CMAKE_BUILDDIR}" -A "${LIBXML2_LIBXSLT_CMAKE_ARCH}" -G "${LIBXML2_LIBXSLT_CMAKE_GENERATOR}" \ -D CMAKE_MSVC_RUNTIME_LIBRARY="${LIBXML2_LIBXSLT_CMAKE_RUNTIME}" \ -D BUILD_SHARED_LIBS="${LIBXML2_LIBXSLT_CMAKE_SHARED_LIBS}" \ - -D CMAKE_PREFIX_PATH="${top_install_dir}" \ - -D CMAKE_INSTALL_PREFIX="${libxslt_install_dir}" \ + -D CMAKE_PREFIX_PATH="${libxml2_install_dir_win}" \ + -D CMAKE_INSTALL_PREFIX="${libxslt_install_dir_win}" \ -D LIBXSLT_WITH_PYTHON=OFF \ -D LIBXSLT_WITH_TESTS=OFF if [ $? -ne 0 ]; then @@ -194,7 +204,7 @@ function build_openssl { fi # Build it. - cd "${work_dir}" + cd "${work_dir}" || return 1 rm -rf "${work_dir}\\${full_name}" "${openssl_install_dir}" if [ ! -f "${full_name}.tar.gz" ] ; then @@ -211,7 +221,7 @@ function build_openssl { OLD_PATH="$PATH" PATH="$PATH;$PERL_PATH" cd "${full_name}" || return 1 - perl Configure no-unit-test --prefix="${openssl_install_dir}" ${OPENSSL_XMLSEC_CONFIG} VC-WIN64A-HYBRIDCRT + perl Configure no-unit-test --prefix="${openssl_install_dir_win}" ${OPENSSL_XMLSEC_CONFIG} VC-WIN64A-HYBRIDCRT rc=$? PATH="$OLD_PATH" if [ $rc -ne 0 ]; then @@ -250,7 +260,7 @@ function build_xmlsec { fi # Build it. - cd "${work_dir}" + cd "${work_dir}" || return 1 rm -rf "${work_dir}\\${full_name_without_rc}" "${xmlsec_install_dir}" if [ ! -f "${full_name}.tar.gz" ] ; then @@ -267,9 +277,9 @@ function build_xmlsec { cd "${full_name_without_rc}\win32" || return 1 powershell -ExecutionPolicy Bypass -File configure.ps1 pedantic=yes static=no unicode=yes ${XMLSEC_CONFIG_OPTIONS}\ xslt=yes crypto=openssl,mscng \ - prefix="${xmlsec_install_dir}" \ - include="${libxml2_install_dir}\include;${libxml2_install_dir}\include\libxml2;${libxslt_install_dir}\include;${openssl_install_dir}\include" \ - lib="${libxml2_install_dir}\lib;${libxslt_install_dir}\lib;${openssl_install_dir}\lib" + prefix="${xmlsec_install_dir_win}" \ + include="${libxml2_install_dir_win}\include;${libxml2_install_dir_win}\include\libxml2;${libxslt_install_dir_win}\include;${openssl_install_dir_win}\include" \ + lib="${libxml2_install_dir_win}\lib;${libxslt_install_dir_win}\lib;${openssl_install_dir_win}\lib" if [ $? -ne 0 ]; then return $? fi @@ -293,9 +303,14 @@ function build_xmlsec { function create_readme { echo "*** Creating README..." cd "${orig_pwd}" || return 1 - cat "${script_dir}\\README-WINDOWS.md.in" | sed "s/@libxml2_version@/${libxml2_version}/g" | sed "s/@libxslt_version@/${libxslt_version}/g" | sed "s/@openssl_version@/${openssl_version}/g" | sed "s/@xmlsec_version@/${xmlsec_version}/g" > "${top_install_dir}\\README.md" - if [ $? -ne 0 ]; then - return $? + cat "${script_dir}\\README-WINDOWS.md.in" | \ + sed "s/@libxml2_version@/${libxml2_version}/g" | \ + sed "s/@libxslt_version@/${libxslt_version}/g" | \ + sed "s/@openssl_version@/${openssl_version}/g" | \ + sed "s/@xmlsec_version@/${xmlsec_version}/g" > "${top_install_dir}\\README.md" + rc=$? + if [ $rc -ne 0 ]; then + return $rc fi echo "*** Done with README!!!" return 0 @@ -319,6 +334,9 @@ function create_distro { return 0 } +# Ensure the log file's directory exists; otherwise the first ">> ${LOG_FILE}" +# redirection would fail and abort the build with a misleading error. +mkdir -p "$(dirname "${LOG_FILE}")" || { echo "Error: failed to create log directory for \"${LOG_FILE}\""; exit 1; } rm -f "${LOG_FILE}" echo "*** LOG FILE: \"${LOG_FILE}\"" diff --git a/scripts/gen-github-wiki.py b/scripts/gen-github-wiki.py index 2eea306d8..782d5edb4 100755 --- a/scripts/gen-github-wiki.py +++ b/scripts/gen-github-wiki.py @@ -164,12 +164,30 @@ def fix_outside_inline_code(text): return "".join(segments) +# Image file extensions that are copied into the wiki. Non-image files (e.g. a +# source "diagrams.sxd") are skipped. +IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".bmp"} + + +def _is_image_file(path): + """Return True if the file has a known image extension.""" + return os.path.splitext(path)[1].lower() in IMAGE_EXTENSIONS + + def collect_images(md_folder): """ - List image files stored in the images/ subfolders of md_folder. - Returns a list of absolute image paths. + List image files stored in a top-level images/ directory and in the + images/ subfolders of md_folder. Returns a list of absolute image paths. """ images = [] + # Top-level images/ directory (for images referenced by top-level pages). + top_images_path = os.path.join(md_folder, "images") + if os.path.isdir(top_images_path): + for img in os.listdir(top_images_path): + img_path = os.path.join(top_images_path, img) + if os.path.isfile(img_path) and _is_image_file(img_path): + images.append(img_path) + # Subfolder images/ directories. for subfolder in os.listdir(md_folder): subfolder_path = os.path.join(md_folder, subfolder) if not os.path.isdir(subfolder_path): @@ -178,7 +196,7 @@ def collect_images(md_folder): if os.path.isdir(images_path): for img in os.listdir(images_path): img_path = os.path.join(images_path, img) - if os.path.isfile(img_path): + if os.path.isfile(img_path) and _is_image_file(img_path): images.append(img_path) return images @@ -225,10 +243,12 @@ def main(): print(f"Temporary build directory: {tmp_dir}") try: - # Run autogen.sh from srcdir inside tmp_dir + # Run autogen.sh from srcdir inside tmp_dir. Only the docs target is + # needed (it does not depend on the compiled library), so skip the full + # library build to keep generation fast and avoid failing on unrelated + # build components or crypto-backend dependencies. print("\n--- Running autogen.sh ---") - jobs = os.cpu_count() or 2 - build_cmd = f"{shlex.quote(autogen_sh)} && make -j{jobs} && make -C docs docs" + build_cmd = f"{shlex.quote(autogen_sh)} && make -C docs docs" run_command( build_cmd, cwd=tmp_dir, diff --git a/src/gnutls/x509vfy.c b/src/gnutls/x509vfy.c index dead131ba..b8aef6222 100644 --- a/src/gnutls/x509vfy.c +++ b/src/gnutls/x509vfy.c @@ -1182,7 +1182,7 @@ int xmlSecGnuTLSX509StoreVerifyCrl( xmlSecKeyDataStorePtr store, gnutls_x509_crl_t crl, - xmlSecKeyInfoCtx* keyInfoCtx + xmlSecKeyInfoCtxPtr keyInfoCtx ) { return(xmlSecGnuTLSX509StoreVerifyCrlInternal(store, crl, NULL, keyInfoCtx)); } diff --git a/src/mscng/ciphers_cbc.c b/src/mscng/ciphers_cbc.c index 6f5268b73..13908c1fa 100644 --- a/src/mscng/ciphers_cbc.c +++ b/src/mscng/ciphers_cbc.c @@ -719,7 +719,7 @@ xmlSecMSCngCbcBlockCipherCtxFinal(xmlSecMSCngCbcBlockCipherCtxPtr ctx, return(-1); } if(inSize < outBuf[blockSize - 1]) { - xmlSecInvalidSizeLessThanError("Input data padding", inSize, outBuf[blockSize - 1], cipherName); + xmlSecInvalidSizeLessThanError("Input data padding", inSize, (xmlSecSize)(outBuf[blockSize - 1]), cipherName); return(-1); } outSize += (inSize - outBuf[blockSize - 1]);