Skip to content

Commit

Permalink
Add URI Signing cdnistd Claim Implementation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dsouza550 committed May 14, 2021
1 parent 258a413 commit 70da95c
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 16 deletions.
4 changes: 2 additions & 2 deletions plugins/experimental/uri_signing/README.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down Expand Up @@ -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`: Renwal token cookies will have cdnistd path segments of the request in their path attribute.

### Unsupported Claims

Expand Down
78 changes: 70 additions & 8 deletions plugins/experimental/uri_signing/jwt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -78,7 +77,6 @@ jwt_delete(struct jwt *jwt)
}

json_decref(jwt->aud);
json_decref(jwt->raw);
free(jwt);
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
4 changes: 2 additions & 2 deletions plugins/experimental/uri_signing/jwt.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
#include <jansson.h>

struct jwt {
json_t *raw;
const char *iss;
const char *sub;
json_t *aud;
Expand All @@ -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);
2 changes: 2 additions & 0 deletions plugins/experimental/uri_signing/python_signer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 5 additions & 0 deletions plugins/experimental/uri_signing/python_signer/uri_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,18 @@ 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]
if args.exp:
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion plugins/experimental/uri_signing/uri_signing.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit 70da95c

Please sign in to comment.