From 73ba3a7a6d1910b5faa4c56a829dbb6e13b6c4c3 Mon Sep 17 00:00:00 2001 From: dsouza550 Date: Tue, 11 May 2021 14:33:45 -0600 Subject: [PATCH] Add URI Signing cdnistd Claim Implementation Renewal tokens issued via the set-cookie response header now have an associated path attribute as documented here https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies. How that path attribure is generated is as according to spec on the latest URI Signing RFC. https://datatracker.ietf.org/doc/html/draft-ietf-cdni-uri-signing Also update associated documentation and python uri signing script to support cdnistd. --- plugins/experimental/uri_signing/README.md | 4 +- plugins/experimental/uri_signing/jwt.c | 78 +++++++++++++++++-- plugins/experimental/uri_signing/jwt.h | 4 +- .../uri_signing/python_signer/README.md | 2 + .../python_signer/example_config.json | 5 +- .../uri_signing/python_signer/uri_signer.py | 5 ++ .../unit_tests/uri_signing_test.cc | 2 +- .../experimental/uri_signing/uri_signing.c | 2 +- 8 files changed, 86 insertions(+), 16 deletions(-) diff --git a/plugins/experimental/uri_signing/README.md b/plugins/experimental/uri_signing/README.md index 02d7c20f4a2..cf0bb558113 100644 --- a/plugins/experimental/uri_signing/README.md +++ b/plugins/experimental/uri_signing/README.md @@ -1,7 +1,7 @@ URI Signing Plugin ================== -This remap plugin implements the draft URI Signing protocol documented [here](https://tools.ietf.org/html/draft-ietf-cdni-uri-signing-16): +This remap plugin implements the draft URI Signing protocol documented [here](https://tools.ietf.org/html/draft-ietf-cdni-uri-signing-21): It takes a single argument: the name of a config file that contains key information. @@ -130,7 +130,7 @@ The following claims are understood: - `cdniuc`: Validated last, after key verificationD. **Only `regex` is supported!** - `cdniets`: If cdnistt is 1, this must be present and non-zero. - `cdnistt`: If present, must be 1. - - `cdnistd`: If present, must be 0. + - `cdnistd`: Renewal token cookies will have cdnistd path segments of the request in their path attribute. ### Unsupported Claims diff --git a/plugins/experimental/uri_signing/jwt.c b/plugins/experimental/uri_signing/jwt.c index f14ecb6e289..1ba45f6225a 100644 --- a/plugins/experimental/uri_signing/jwt.c +++ b/plugins/experimental/uri_signing/jwt.c @@ -52,7 +52,6 @@ parse_jwt(json_t *raw) } struct jwt *jwt = malloc(sizeof *jwt); - jwt->raw = raw; jwt->iss = json_string_value(json_object_get(raw, "iss")); jwt->sub = json_string_value(json_object_get(raw, "sub")); jwt->aud = json_object_get(raw, "aud"); @@ -78,7 +77,6 @@ jwt_delete(struct jwt *jwt) } json_decref(jwt->aud); - json_decref(jwt->raw); free(jwt); } @@ -141,7 +139,7 @@ jwt_validate(struct jwt *jwt) return false; } - if (jwt->cdnistd != 0) { + if (jwt->cdnistd < 0) { PluginDebug("Initial JWT Failure: unsupported value for cdnistd: %d", jwt->cdnistd); return false; } @@ -263,7 +261,7 @@ void renew_copy_raw(json_t *new_json, const char *name, json_t *old_json) { if (old_json) { - json_object_set_new(new_json, name, old_json); + json_object_set(new_json, name, old_json); } } @@ -283,7 +281,7 @@ renew_copy_integer(json_t *new_json, const char *name, double old) } char * -renew(struct jwt *jwt, const char *iss, cjose_jwk_t *jwk, const char *alg, const char *package) +renew(struct jwt *jwt, const char *iss, cjose_jwk_t *jwk, const char *alg, const char *package, const char *uri, size_t uri_ct) { char *s = NULL; if (jwt->cdnistt != 1) { @@ -296,6 +294,65 @@ renew(struct jwt *jwt, const char *iss, cjose_jwk_t *jwk, const char *alg, const return NULL; } + int buff_ct = uri_ct + 2; + int normal_err; + char *normal_uri = (char *)TSmalloc(buff_ct); + memset(normal_uri, 0, buff_ct); + + normal_err = normalize_uri(uri, uri_ct, normal_uri, buff_ct); + + if (normal_err) { + goto fail_normal; + } + + /* Determine Path String Based on cdnistd claim */ + size_t normal_size = strlen(normal_uri); + const char *path_start = normal_uri; + const char *path_end = NULL; + const char *uri_end = normal_uri + normal_size; + char *path_string = NULL; + size_t path_size = normal_size + 1; + + path_string = (char *)TSmalloc(path_size); + memset(path_string, 0, path_size); + PluginDebug("Renewing JWT. Stripped URI: %s", uri); + + if (jwt->cdnistd == 0) { + PluginDebug("STD is 0 - Setting Cookie Path to Path=/"); + snprintf(path_string, 2, "%s", "/"); + } else { + PluginDebug("STD is greater than 0. Calculating Path"); + int slash_count = 0; + /* Search for 3rd '/' to mark start of path */ + while (path_start != uri_end && slash_count < 3) { + ++path_start; + if (*path_start == '/') { + slash_count++; + } + } + if (path_start == uri_end) { + PluginDebug("STD is greater than number of path segments. Cannot Renew Token!"); + goto fail_path; + } + PluginDebug("Searching through path: %s", path_start); + /* Now search through path for cdnistd number of segments */ + slash_count = 0; + path_end = path_start + 1; + while (path_end != uri_end && slash_count < jwt->cdnistd) { + ++path_end; + if (*path_end == '/') { + slash_count++; + } + } + if (path_end == uri_end) { + PluginDebug("STD is greater than number of path segments. Cannot Renew Token!"); + goto fail_path; + } + path_size = path_end - path_start + 1; + snprintf(path_string, path_size, "%s", path_start); + PluginDebug("Setting Cookie Path to %s", path_string); + } + json_t *new_json = json_object(); renew_copy_string(new_json, "iss", iss); /* use issuer of new signing key */ renew_copy_string(new_json, "sub", jwt->sub); @@ -348,15 +405,20 @@ renew(struct jwt *jwt, const char *iss, cjose_jwk_t *jwk, const char *alg, const goto fail_jws; } - const char *fmt = "%s=%s"; + const char *fmt = "%s=%s; Path=%s"; size_t s_ct; - s = malloc(s_ct = (1 + snprintf(NULL, 0, fmt, package, jws_str))); - snprintf(s, s_ct, fmt, package, jws_str); + s = malloc(s_ct = (1 + snprintf(NULL, 0, fmt, package, jws_str, path_string))); + snprintf(s, s_ct, fmt, package, jws_str, path_string); + PluginDebug("Cookie returned from renew function: %s", s); fail_jws: cjose_jws_release(jws); fail_hdr: cjose_header_release(hdr); fail_json: free(pt); +fail_path: + TSfree(path_string); +fail_normal: + TSfree(normal_uri); return s; } diff --git a/plugins/experimental/uri_signing/jwt.h b/plugins/experimental/uri_signing/jwt.h index 1e4d58fefda..6f61fc406f6 100644 --- a/plugins/experimental/uri_signing/jwt.h +++ b/plugins/experimental/uri_signing/jwt.h @@ -22,7 +22,6 @@ #include struct jwt { - json_t *raw; const char *iss; const char *sub; json_t *aud; @@ -45,4 +44,5 @@ bool jwt_check_aud(json_t *aud, const char *id); bool jwt_check_uri(const char *cdniuc, const char *uri); struct _cjose_jwk_int; -char *renew(struct jwt *jwt, const char *iss, struct _cjose_jwk_int *jwk, const char *alg, const char *package); +char *renew(struct jwt *jwt, const char *iss, struct _cjose_jwk_int *jwk, const char *alg, const char *package, const char *uri, + size_t uri_ct); diff --git a/plugins/experimental/uri_signing/python_signer/README.md b/plugins/experimental/uri_signing/python_signer/README.md index daa41bb8f2c..3dddbfbca9f 100644 --- a/plugins/experimental/uri_signing/python_signer/README.md +++ b/plugins/experimental/uri_signing/python_signer/README.md @@ -27,6 +27,8 @@ The config file should be a JSON object that contains the following: - `cdniets`: Must be set if using cdnistt. Provides means of setting Expiry Times when generating subsequent tokens. It denotes the number of seconds to be added to the time at which the JWT is verified that gives the value of the Expiry Time claim of the next signed JWT. + - `cdnistd`: Integer value representing number of path segments that renewal token cookies should valid for. This is used when + generating the path attribute of the cookies containing renewal tokens. - `keys`: A list of json objects, each one representing a key. Each key should have the following attributes: - `alg`: The Cryptographic algorithm to be used with the key. - `kid`: The key identifier diff --git a/plugins/experimental/uri_signing/python_signer/example_config.json b/plugins/experimental/uri_signing/python_signer/example_config.json index 4039796ebad..103edec3520 100644 --- a/plugins/experimental/uri_signing/python_signer/example_config.json +++ b/plugins/experimental/uri_signing/python_signer/example_config.json @@ -2,8 +2,9 @@ "iss": "Example Issuer", "token_lifetime": 90, "aud": "Caching Software", - "cdnistt": true, - "cdniets": 30, + "cdnistt": true, + "cdnistd": 2, + "cdniets": 30, "keys": [ { "alg": "HS256", diff --git a/plugins/experimental/uri_signing/python_signer/uri_signer.py b/plugins/experimental/uri_signing/python_signer/uri_signer.py index b22eed01979..8045a1ee6bf 100755 --- a/plugins/experimental/uri_signing/python_signer/uri_signer.py +++ b/plugins/experimental/uri_signing/python_signer/uri_signer.py @@ -94,6 +94,9 @@ def main(): else: claimset["cdniets"] = 30 + if "cdnistd" in config.keys(): + claimset["cdnistd"] = config["cdnistd"] + # process override args - simple if args.iss: claimset["iss"] = args.iss[0] @@ -101,6 +104,8 @@ def main(): claimset["exp"] = args.exp[0] if args.aud: claimset["aud"] = args.aud[0] + if args.cdnistd: + claimset["cdnistd"] = args.cdnistd[0] # process override args - complex if args.cdnistt: 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 20b2104eda0..92454d67421 100644 --- a/plugins/experimental/uri_signing/unit_tests/uri_signing_test.cc +++ b/plugins/experimental/uri_signing/unit_tests/uri_signing_test.cc @@ -161,7 +161,7 @@ TEST_CASE("1", "[JWSParsingTest]") SECTION("JWT Parsing with unsupported value for cdnistd claim") { - REQUIRE(!jwt_parsing_helper("{\"cdniets\":30,\"cdnistt\":1,\"cdnistd\":4,\"iss\":\"Content Access " + REQUIRE(!jwt_parsing_helper("{\"cdniets\":30,\"cdnistt\":1,\"cdnistd\":-2,\"iss\":\"Content Access " "Manager\",\"cdniuc\":\"uri-regex:http://foobar.local/testDir/*\"}")); } fprintf(stderr, "\n"); diff --git a/plugins/experimental/uri_signing/uri_signing.c b/plugins/experimental/uri_signing/uri_signing.c index d70bd5690b2..3f14365f21d 100644 --- a/plugins/experimental/uri_signing/uri_signing.c +++ b/plugins/experimental/uri_signing/uri_signing.c @@ -328,7 +328,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) /* There has been a validated JWT found in either the cookie or url */ struct signer *signer = config_signer((struct config *)ih); - char *cookie = renew(jwt, signer->issuer, signer->jwk, signer->alg, package); + char *cookie = renew(jwt, signer->issuer, signer->jwk, signer->alg, package, strip_uri, strip_ct); jwt_delete(jwt); if (cpi < max_cpi) {