Skip to content

Commit 009c8e3

Browse files
author
Dylan Souza
committed
Implement aud claim in Uri Signing Plugin
The Aud claim is implemented as per the RFC version 16 that can be found here:https://tools.ietf.org/html/draft-ietf-cdni-uri-signing-16 As per the specification, the aud claim can be either a JSON array or a string. The aud claim is stored as raw json in the jwt class in this implementation. It is converted either to an array or a string at validation time. This commit also expands the unit tests quite a bit. Test configs can be provided in the unit_tests directory and parsed in the test framework. JWS validation is also testable now. This commit also fixes two memory leaks 1. Issuers were never being freed on configuration cleanup. 2. Token renewal allocates a tmp json_object without freeing.
1 parent 0b39c1d commit 009c8e3

File tree

7 files changed

+345
-8
lines changed

7 files changed

+345
-8
lines changed

plugins/experimental/uri_signing/config.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ struct config {
4545
char **issuer_names;
4646
struct signer signer;
4747
struct auth_directive *auth_directives;
48+
char *id;
4849
};
4950

5051
cjose_jwk_t **
@@ -80,6 +81,12 @@ find_key_by_kid(struct config *cfg, const char *issuer, const char *kid)
8081
return NULL;
8182
}
8283

84+
const char *
85+
config_get_id(struct config *cfg)
86+
{
87+
return cfg->id;
88+
}
89+
8390
struct config *
8491
config_new(size_t n)
8592
{
@@ -105,6 +112,7 @@ config_new(size_t n)
105112
cfg->signer.alg = NULL;
106113

107114
cfg->auth_directives = NULL;
115+
cfg->id = NULL;
108116

109117
PluginDebug("New config object created at %p", cfg);
110118
return cfg;
@@ -117,6 +125,7 @@ config_delete(struct config *cfg)
117125
return;
118126
}
119127
hdestroy_r(cfg->issuers);
128+
free(cfg->issuers);
120129

121130
for (cjose_jwk_t ***jwkis = cfg->jwkis; *jwkis; ++jwkis) {
122131
for (cjose_jwk_t **jwks = *jwkis; *jwks; ++jwks) {
@@ -126,6 +135,10 @@ config_delete(struct config *cfg)
126135
}
127136
free(cfg->jwkis);
128137

138+
if (cfg->id) {
139+
free(cfg->id);
140+
}
141+
129142
for (char **name = cfg->issuer_names; *name; ++name) {
130143
free(*name);
131144
}
@@ -259,6 +272,18 @@ read_config(const char *path)
259272
renewal_kid = json_string_value(renewal_kid_json);
260273
}
261274

275+
json_t *id_json = json_object_get(jwks, "id");
276+
const char *id;
277+
if (id_json) {
278+
id = json_string_value(id_json);
279+
if (id) {
280+
cfg->id = malloc(strlen(id) + 1);
281+
strcpy(cfg->id, id);
282+
PluginDebug("Found Id in the config: %s", cfg->id);
283+
}
284+
}
285+
json_decref(id_json);
286+
262287
size_t jwks_ct = json_array_size(key_ary);
263288
cjose_jwk_t **jwks = (*jwkis++ = malloc((jwks_ct + 1) * sizeof *jwks));
264289
PluginDebug("Created table with size %d", cfg->issuers->size);

plugins/experimental/uri_signing/config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ struct signer *config_signer(struct config *);
3333
struct _cjose_jwk_int **find_keys(struct config *cfg, const char *issuer);
3434
struct _cjose_jwk_int *find_key_by_kid(struct config *cfg, const char *issuer, const char *kid);
3535
bool uri_matches_auth_directive(struct config *cfg, const char *uri, size_t uri_ct);
36+
const char *config_get_id(struct config *cfg);

plugins/experimental/uri_signing/jwt.c

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ parse_jwt(json_t *raw)
5656
jwt->raw = raw;
5757
jwt->iss = json_string_value(json_object_get(raw, "iss"));
5858
jwt->sub = json_string_value(json_object_get(raw, "sub"));
59-
jwt->aud = json_string_value(json_object_get(raw, "aud"));
59+
jwt->aud = json_object_get(raw, "aud");
6060
jwt->exp = parse_number(json_object_get(raw, "exp"));
6161
jwt->nbf = parse_number(json_object_get(raw, "nbf"));
6262
jwt->iat = parse_number(json_object_get(raw, "iat"));
@@ -77,6 +77,8 @@ jwt_delete(struct jwt *jwt)
7777
if (!jwt) {
7878
return;
7979
}
80+
81+
json_decref(jwt->aud);
8082
json_decref(jwt->raw);
8183
free(jwt);
8284
}
@@ -116,11 +118,6 @@ jwt_validate(struct jwt *jwt)
116118
return false;
117119
}
118120

119-
if (!unsupported_string_claim(jwt->aud)) {
120-
PluginDebug("Initial JWT Failure: aud unsupported");
121-
return false;
122-
}
123-
124121
if (now() > jwt->exp) {
125122
PluginDebug("Initial JWT Failure: expired token");
126123
return false;
@@ -159,6 +156,43 @@ jwt_validate(struct jwt *jwt)
159156
return true;
160157
}
161158

159+
bool
160+
jwt_check_aud(json_t *aud, const char *id)
161+
{
162+
if (!aud) {
163+
return true;
164+
}
165+
if (!id) {
166+
return false;
167+
}
168+
/* If aud is a string, do a simple string comparison */
169+
const char *aud_str = json_string_value(aud);
170+
if (aud_str) {
171+
PluginDebug("Checking aud %s agaisnt token aud string \"%s\"", id, aud_str);
172+
/* Both strings will be null terminated per jansson docs */
173+
if (strcmp(aud_str, id) == 0) {
174+
return true;
175+
}
176+
return false;
177+
}
178+
PluginDebug("Checking aud %s agaisnt token aud array", id);
179+
/* If aud is an array, check all items */
180+
if (json_is_array(aud)) {
181+
size_t index;
182+
json_t *aud_item;
183+
json_array_foreach(aud, index, aud_item)
184+
{
185+
aud_str = json_string_value(aud_item);
186+
if (aud_str) {
187+
if (strcmp(aud_str, id) == 0) {
188+
return true;
189+
}
190+
}
191+
}
192+
}
193+
return false;
194+
}
195+
162196
bool
163197
jwt_check_uri(const char *cdniuc, const char *uri)
164198
{
@@ -219,6 +253,14 @@ renew_copy_string(json_t *new_json, const char *name, const char *old)
219253
}
220254
}
221255

256+
void
257+
renew_copy_raw(json_t *new_json, const char *name, json_t *old_json)
258+
{
259+
if (old_json) {
260+
json_object_set_new(new_json, name, old_json);
261+
}
262+
}
263+
222264
void
223265
renew_copy_real(json_t *new_json, const char *name, double old)
224266
{
@@ -251,7 +293,7 @@ renew(struct jwt *jwt, const char *iss, cjose_jwk_t *jwk, const char *alg, const
251293
json_t *new_json = json_object();
252294
renew_copy_string(new_json, "iss", iss); /* use issuer of new signing key */
253295
renew_copy_string(new_json, "sub", jwt->sub);
254-
renew_copy_string(new_json, "aud", jwt->aud);
296+
renew_copy_raw(new_json, "aud", jwt->aud);
255297
renew_copy_real(new_json, "exp", now() + jwt->cdniets); /* expire ets seconds hence */
256298
renew_copy_real(new_json, "nbf", jwt->nbf);
257299
renew_copy_real(new_json, "iat", now()); /* issued now */
@@ -261,6 +303,7 @@ renew(struct jwt *jwt, const char *iss, cjose_jwk_t *jwk, const char *alg, const
261303
renew_copy_integer(new_json, "cdnistt", jwt->cdnistt);
262304

263305
char *pt = json_dumps(new_json, JSON_COMPACT);
306+
json_decref(new_json);
264307

265308
cjose_header_t *hdr = cjose_header_new(NULL);
266309
if (!hdr) {

plugins/experimental/uri_signing/jwt.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct jwt {
2323
json_t *raw;
2424
const char *iss;
2525
const char *sub;
26-
const char *aud;
26+
json_t *aud;
2727
double exp;
2828
double nbf;
2929
double iat;
@@ -39,6 +39,7 @@ struct jwt {
3939
struct jwt *parse_jwt(json_t *raw);
4040
void jwt_delete(struct jwt *jwt);
4141
bool jwt_validate(struct jwt *jwt);
42+
bool jwt_check_aud(json_t *aud, const char *id);
4243
bool jwt_check_uri(const char *cdniuc, const char *uri);
4344

4445
struct _cjose_jwk_int;

plugins/experimental/uri_signing/parse.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ validate_jws(cjose_jws_t *jws, struct config *cfg, const char *uri, size_t uri_c
229229
}
230230
}
231231

232+
if (!jwt_check_aud(jwt->aud, config_get_id(cfg))) {
233+
PluginDebug("Valid key for %16p that does not match aud.", jws);
234+
goto jwt_fail;
235+
}
236+
232237
if (!jwt_check_uri(jwt->cdniuc, uri)) {
233238
PluginDebug("Valid key for %16p that does not match uri.", jws);
234239
goto jwt_fail;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
{
2+
"Master Issuer": {
3+
"renewal_kid": "6",
4+
"id": "tester",
5+
"auth_directives": [
6+
{
7+
"auth": "allow",
8+
"uri": "regex:invalid"
9+
}
10+
],
11+
"keys": [
12+
{
13+
"alg": "HS256",
14+
"k": "nxb7fyO5Z2hGz9E3oKm1357ptvC2su5QwQUb4YaIaIc",
15+
"kid": "0",
16+
"kty": "oct"
17+
},
18+
{
19+
"alg": "HS256",
20+
"k": "cXKukBqFvQ0n3WAuRnWfExC14dmHdGoJULoZjGu9tJC",
21+
"kid": "1",
22+
"kty": "oct"
23+
},
24+
{
25+
"alg": "HS256",
26+
"k": "38pJlSXfX87jWL0a03luml9QzUmM4qts1nmfIHA3B7r",
27+
"kid": "2",
28+
"kty": "oct"
29+
},
30+
{
31+
"alg": "HS256",
32+
"k": "zNQPphknDGvzR5kA7IonXIDWKMyB1b8NpGmmDNlpgtM",
33+
"kid": "3",
34+
"kty": "oct"
35+
},
36+
{
37+
"alg": "HS256",
38+
"k": "iB2ogCmQRt7r5hW7pgyP5FqiFcCl53MPQvfXv8wrZAn",
39+
"kid": "4",
40+
"kty": "oct"
41+
},
42+
{
43+
"alg": "HS256",
44+
"k": "GJMCTyZhNoSOZvUOKmmY9MtGSLaONNLHqtKwsC3MWKo",
45+
"kid": "5",
46+
"kty": "oct"
47+
},
48+
{
49+
"alg": "HS256",
50+
"k": "u2LziZKJFBnOfjUQUmvot7C9t91jj7ocJPIU9aDdbUl",
51+
"kid": "6",
52+
"kty": "oct"
53+
},
54+
{
55+
"alg": "HS256",
56+
"k": "DRBKrBh87NYkH3UzfW1tWbiXCYXiYGZUE9w1orZngL0",
57+
"kid": "7",
58+
"kty": "oct"
59+
},
60+
{
61+
"alg": "HS256",
62+
"k": "KNNKFbun8lEs7GbiKlo9mYGNdvpt33tdFzHbNnasDyP",
63+
"kid": "8",
64+
"kty": "oct"
65+
},
66+
{
67+
"alg": "HS256",
68+
"k": "yb6kOddMUdupPRSkWMUdE6jrWT4MqUnVyTjpeJBYIqp",
69+
"kid": "9",
70+
"kty": "oct"
71+
}
72+
]
73+
},
74+
"Second Issuer": {
75+
"keys": [
76+
{
77+
"alg": "HS256",
78+
"k": "testkey1",
79+
"kid": "one",
80+
"kty": "oct"
81+
},
82+
{
83+
"alg": "HS256",
84+
"k": "testkey2",
85+
"kid": "two",
86+
"kty": "oct"
87+
},
88+
{
89+
"alg": "HS256",
90+
"k": "testkey3",
91+
"kid": "three",
92+
"kty": "oct"
93+
},
94+
{
95+
"alg": "HS256",
96+
"k": "testkey4",
97+
"kid": "four",
98+
"kty": "oct"
99+
}
100+
]
101+
}
102+
}

0 commit comments

Comments
 (0)