diff --git a/plugins/experimental/uri_signing/parse.c b/plugins/experimental/uri_signing/parse.c index 99ded7b71ab..099acdb315f 100644 --- a/plugins/experimental/uri_signing/parse.c +++ b/plugins/experimental/uri_signing/parse.c @@ -29,10 +29,11 @@ #include cjose_jws_t * -get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName) +get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName, char *strip_uri, size_t buff_ct, size_t *strip_ct) { /* Reserved characters as defined by the URI Generic Syntax RFC: https://tools.ietf.org/html/rfc3986#section-2.2 */ - const char *reserved_string = ":/?#[]@!$&\'()*+,;="; + static const char *reserved_string = ":/?#[]@!$&\'()*+,;="; + static const char *sub_delim_string = "!$&\'()*+,;="; /* If param name ends in reserved character this will be treated as the termination symbol when parsing for package. Default is * '='. */ @@ -94,6 +95,29 @@ get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName) PluginDebug("Unable to read JWS: %.*s, %s", (int)(key_end - key), key, err.message ? err.message : ""); } else { PluginDebug("Parsed JWS: %.*s (%16p)", (int)(key_end - key), key, jws); + + /* Strip token */ + /* Check that passed buffer is large enough */ + *strip_ct = ((key - uri) + (end - value_end)); + if (buff_ct <= *strip_ct) { + PluginDebug("Strip URI buffer is not large enough"); + return NULL; + } + + if (value_end != end && strchr(sub_delim_string, *value_end)) { + /*Strip from first char of package name to sub-delimeter that terminates the signed JWT */ + memcpy(strip_uri, uri, (key - uri)); + memcpy(strip_uri + (key - uri), value_end + 1, (end - value_end + 1)); + } else { + /*Strip from reserved char to the last char of the JWT */ + memcpy(strip_uri, uri, (key - uri - 1)); + memcpy(strip_uri + (key - uri - 1), value_end, (end - value_end)); + } + + if (strip_uri[*strip_ct - 1] != '\0') { + strip_uri[*strip_ct - 1] = '\0'; + } + PluginDebug("Stripped URI: %s", strip_uri); } return jws; } diff --git a/plugins/experimental/uri_signing/parse.h b/plugins/experimental/uri_signing/parse.h index 8d82c63ea7f..d05bfd59adb 100644 --- a/plugins/experimental/uri_signing/parse.h +++ b/plugins/experimental/uri_signing/parse.h @@ -19,7 +19,8 @@ #include struct _cjose_jws_int; -struct _cjose_jws_int *get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName); +struct _cjose_jws_int *get_jws_from_uri(const char *uri, size_t uri_ct, const char *paramName, char *strip_uri, size_t buff_ct, + size_t *strip_ct); struct _cjose_jws_int *get_jws_from_cookie(const char **cookie, size_t *cookie_ct, const char *paramName); struct config; diff --git a/plugins/experimental/uri_signing/unit_tests/uri_signing_test.cc b/plugins/experimental/uri_signing/unit_tests/uri_signing_test.cc index 30b8681aefc..b879f7ce449 100644 --- a/plugins/experimental/uri_signing/unit_tests/uri_signing_test.cc +++ b/plugins/experimental/uri_signing/unit_tests/uri_signing_test.cc @@ -28,6 +28,7 @@ extern "C" { #include #include "../jwt.h" #include "../normalize.h" +#include "../parse.h" } bool @@ -92,6 +93,28 @@ remove_dot_helper(const char *path, const char *expected_path) } } +bool +jws_parsing_helper(const char *uri, const char *paramName, const char *expected_strip) +{ + bool resp; + size_t uri_ct = strlen(uri); + size_t strip_ct = 0; + char uri_strip[uri_ct + 1]; + memset(uri_strip, 0, sizeof uri_strip); + cjose_jws_t *jws = get_jws_from_uri(uri, uri_ct, paramName, uri_strip, uri_ct, &strip_ct); + if (jws) { + resp = true; + if (strcmp(uri_strip, expected_strip) != 0) { + cjose_jws_release(jws); + resp = false; + } + } else { + resp = false; + } + cjose_jws_release(jws); + return resp; +} + TEST_CASE("1", "[JWSParsingTest]") { INFO("TEST 1, Test JWT Parsing From Token Strings"); @@ -136,6 +159,157 @@ TEST_CASE("1", "[JWSParsingTest]") fprintf(stderr, "\n"); } +TEST_CASE("2", "[JWSFromURLTest]") +{ + INFO("TEST 2, Test JWT Parsing and Stripping From URLs"); + + SECTION("Token at end of URI") + { + REQUIRE(jws_parsing_helper( + "www.foo.com/hellothere/" + "URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + "URISigningPackage", "www.foo.com/hellothere")); + } + + SECTION("No Token in URL") + { + REQUIRE(!jws_parsing_helper( + "www.foo.com/hellothere/" + "URISigningPackag=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + "URISigningPackage", NULL)); + } + + SECTION("Token in middle of the URL") + { + REQUIRE(jws_parsing_helper("www.foo.com/hellothere/" + "URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/Something/Else", + "URISigningPackage", "www.foo.com/hellothere/Something/Else")); + } + + SECTION("Token at the start of the URL") + { + REQUIRE(jws_parsing_helper(":URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/www.foo.com/hellothere/Something/Else", + "URISigningPackage", "/www.foo.com/hellothere/Something/Else")); + } + + SECTION("Pass empty path parameter at end") + { + REQUIRE(!jws_parsing_helper("www.foobar.com/hellothere/URISigningPackage=", "URISigningPackage", NULL)); + } + + SECTION("Pass empty path parameter in the middle of URL") + { + REQUIRE(!jws_parsing_helper("www.foobar.com/hellothere/URISigningPackage=/Something/Else", "URISigningPackage", NULL)); + } + + SECTION("Partial package name in previous path parameter") + { + REQUIRE(jws_parsing_helper("www.foobar.com/URISig/" + "URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/Something/Else", + "URISigningPackage", "www.foobar.com/URISig/Something/Else")); + } + + SECTION("Package comes directly after two reserved characters") + { + REQUIRE(jws_parsing_helper("www.foobar.com/" + ":URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/Something/Else", + "URISigningPackage", "www.foobar.com//Something/Else")); + } + + SECTION("Package comes directly after string of reserved characters") + { + REQUIRE(jws_parsing_helper("www.foobar.com/?!/" + ":URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/Something/Else", + "URISigningPackage", "www.foobar.com/?!//Something/Else")); + } + + SECTION("Invalid token passed before a valid token") + { + REQUIRE(!jws_parsing_helper("www.foobar.com/URISigningPackage=/" + "URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/Something/Else", + "URISigningPackage", NULL)); + } + + SECTION("Empty string as URL") { REQUIRE(!jws_parsing_helper("", "URISigningPackage", NULL)); } + + SECTION("Empty package name to parser") + { + REQUIRE(!jws_parsing_helper( + "www.foobar.com/" + "URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + "", NULL)); + } + + SECTION("Custom package name with a reserved character - at the end of the URI") + { + REQUIRE(jws_parsing_helper( + "www.foobar.com/CustomPackage/" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + "CustomPackage/", "www.foobar.com")); + } + + SECTION("Custom package name with a reserved character - in the middle of the URI") + { + REQUIRE(jws_parsing_helper( + "www.foobar.com/CustomPackage/" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c/Something/Else", + "CustomPackage/", "www.foobar.com/Something/Else")); + } + + SECTION("URI signing package passed as the only a query parameter") + { + REQUIRE(jws_parsing_helper( + "www.foobar.com/Something/" + "Here?URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + "URISigningPackage", "www.foobar.com/Something/Here")); + } + + SECTION("URI signing package passed as first of many query parameters") + { + REQUIRE(jws_parsing_helper("www.foobar.com/Something/" + "Here?URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&query3=foobar&query1=foo&query2=bar", + "URISigningPackage", "www.foobar.com/Something/Here?query3=foobar&query1=foo&query2=bar")); + } + + SECTION("URI signing package passed as one of many query parameters - passed in middle") + { + REQUIRE(jws_parsing_helper("www.foobar.com/Something/" + "Here?query1=foo&query2=bar&URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c&query3=foobar", + "URISigningPackage", "www.foobar.com/Something/Here?query1=foo&query2=bar&query3=foobar")); + } + + SECTION("URI signing package passed as last of many query parameters") + { + REQUIRE(jws_parsing_helper("www.foobar.com/Something/" + "Here?query1=foo&query2=bar&URISigningPackage=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ." + "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c", + "URISigningPackage", "www.foobar.com/Something/Here?query1=foo&query2=bar")); + } +} + TEST_CASE("3", "[RemoveDotSegmentsTest]") { INFO("TEST 3, Test Removal of Dot Segments From Paths"); diff --git a/plugins/experimental/uri_signing/uri_signing.c b/plugins/experimental/uri_signing/uri_signing.c index 044731b98a4..fadd269e540 100644 --- a/plugins/experimental/uri_signing/uri_signing.c +++ b/plugins/experimental/uri_signing/uri_signing.c @@ -175,7 +175,11 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) if (cpi < max_cpi) { checkpoints[cpi++] = mark_timer(&t); } - cjose_jws_t *jws = get_jws_from_uri(url, url_ct, package); + + char strip_uri[2000] = {0}; + size_t strip_ct; + cjose_jws_t *jws = get_jws_from_uri(url, url_ct, package, strip_uri, 2000, &strip_ct); + if (cpi < max_cpi) { checkpoints[cpi++] = mark_timer(&t); } @@ -222,7 +226,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) if (cpi < max_cpi) { checkpoints[cpi++] = mark_timer(&t); } - struct jwt *jwt = validate_jws(jws, (struct config *)ih, url, url_ct); + struct jwt *jwt = validate_jws(jws, (struct config *)ih, strip_uri, strip_ct); cjose_jws_release(jws); if (cpi < max_cpi) { checkpoints[cpi++] = mark_timer(&t);