You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by so...@apache.org on 2018/02/22 04:56:17 UTC

[trafficserver] 01/02: Added auth_directives to uri_signing.

This is an automated email from the ASF dual-hosted git repository.

sorber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit b7d3c83f89b5996e8febcb229040add04f44614b
Author: Chris Lemmons <ch...@comcast.com>
AuthorDate: Wed Feb 21 23:10:18 2018 +0000

    Added auth_directives to uri_signing.
    
    These directives exist to indicate that specific paths should be
    considered "pre-authenticated". This allows users to override specific
    paths where signing should not or cannot be used.
---
 plugins/experimental/uri_signing/README.md     | 29 ++++++++
 plugins/experimental/uri_signing/config.c      | 94 +++++++++++++++++++++++++-
 plugins/experimental/uri_signing/config.h      |  4 ++
 plugins/experimental/uri_signing/jwt.c         |  6 +-
 plugins/experimental/uri_signing/jwt.h         |  2 +-
 plugins/experimental/uri_signing/parse.c       |  2 +-
 plugins/experimental/uri_signing/uri_signing.c |  7 +-
 7 files changed, 136 insertions(+), 8 deletions(-)

diff --git a/plugins/experimental/uri_signing/README.md b/plugins/experimental/uri_signing/README.md
index 7a90bfd..fe242d8 100644
--- a/plugins/experimental/uri_signing/README.md
+++ b/plugins/experimental/uri_signing/README.md
@@ -46,6 +46,35 @@ If there is not precisely one renewal key, the plugin will not load.
 Although the `kid` and `alg` parameters are optional in JWKs generally, both
 members must be present in keys used for URI signing.
 
+### Auth Directives
+
+It's occasionally useful to allow un-signed access to specific paths. To
+that end, the `auth_directives` parameter is supported. It can be used
+like this:
+
+    {
+      "Kabletown URI Authority": {
+        "renewal_kid": "Second Key",
+        "auth_directives": [
+          { auth: "allow", uri: "uri-regex:.*crossdomain.xml" },
+          { auth: "deny",  uri: "uri-regex:https?://[^/]*/public/secret.xml.*" },
+          { auth: "allow", uri: "uri-regex:https?://[^/]*/public/.*" },
+          { auth: "allow", uri: "uri-regex:.*favicon.ico" }
+        ]
+        "keys": [
+          ⋮
+        ]
+    }
+
+Each of the `auth_directives` will be evaluated in order for each url
+that does not have a valid token. If it matches an allowed path before
+it matches a denied one, it will be served anyway. If it matches no
+`auth_directives`, it will not be served.
+
+It's worth noting that multiple issuers can provide `auth_directives`.
+Each issuer will be processed in order and any issuer can provide access to
+a path.
+
 Usage
 -----
 
diff --git a/plugins/experimental/uri_signing/config.c b/plugins/experimental/uri_signing/config.c
index 3d698c5..83083f8 100644
--- a/plugins/experimental/uri_signing/config.c
+++ b/plugins/experimental/uri_signing/config.c
@@ -19,6 +19,7 @@
 #include "uri_signing.h"
 #include "config.h"
 #include "timing.h"
+#include "jwt.h"
 
 #include <ts/ts.h>
 
@@ -31,11 +32,19 @@
 
 #define JSONError(err) PluginError("json-err: %s:%d:%d: %s", (err).source, (err).line, (err).column, (err).text)
 
+#define AUTH_DENY 0
+#define AUTH_ALLOW 1
+struct auth_directive {
+  char auth;
+  char *container;
+};
+
 struct config {
   struct hsearch_data *issuers;
   cjose_jwk_t ***jwkis;
   char **issuer_names;
   struct signer signer;
+  struct auth_directive *auth_directives;
 };
 
 cjose_jwk_t **
@@ -46,6 +55,7 @@ find_keys(struct config *cfg, const char *issuer)
     PluginDebug("Unable to locate any keys at %p for issuer %s in %p->%p", entry, issuer, cfg, cfg->issuers);
     return NULL;
   }
+
   int n = 0;
   for (cjose_jwk_t **jwks = entry->data; *jwks; ++jwks, ++n) {
     ;
@@ -94,6 +104,8 @@ config_new(size_t n)
   cfg->signer.jwk    = NULL;
   cfg->signer.alg    = NULL;
 
+  cfg->auth_directives = NULL;
+
   PluginDebug("New config object created at %p", cfg);
   return cfg;
 }
@@ -122,6 +134,13 @@ config_delete(struct config *cfg)
   if (cfg->signer.alg) {
     free(cfg->signer.alg);
   }
+
+  if (cfg->auth_directives) {
+    for (struct auth_directive *ad = cfg->auth_directives; ad->container; ++ad) {
+      free(ad->container);
+    }
+    free(cfg->auth_directives);
+  }
   free(cfg);
 }
 
@@ -172,7 +191,60 @@ read_config(const char *path)
   json_t *jwks;
   json_object_foreach(issuer_json, json_issuer, jwks)
   {
-    *issuer         = strdup(json_issuer);
+    *issuer = strdup(json_issuer);
+
+    json_t *ad_json = json_object_get(jwks, "auth_directives");
+    if (ad_json) {
+      PluginDebug("Loading auth_directives.");
+      size_t ad_ct = json_array_size(ad_json);
+      if (ad_ct) {
+        PluginDebug("Loading %d new auth_directives.", (int)ad_ct);
+        struct auth_directive *ad = cfg->auth_directives;
+        if (cfg->auth_directives) {
+          /* We've already got directives, so extend them. */
+          PluginDebug("Extending existing auth_directives.");
+          size_t ad_old_ct = 0;
+          while (ad->container) {
+            ++ad;
+            ++ad_old_ct;
+          }
+          cfg->auth_directives = realloc(cfg->auth_directives, (ad_ct + ad_old_ct + 1) * sizeof *cfg->auth_directives);
+          ad                   = cfg->auth_directives + ad_old_ct;
+        } else {
+          ad = cfg->auth_directives = malloc((ad_ct + 1) * sizeof *cfg->auth_directives);
+        }
+        json_t *ad_obj;
+        for (size_t idx = 0; (idx < ad_ct) && (ad_obj = json_array_get(ad_json, idx)); ++idx, ++ad) {
+          json_t *uri_json  = json_object_get(ad_obj, "uri");
+          json_t *auth_json = json_object_get(ad_obj, "auth");
+          if (uri_json) {
+            const char *uri = json_string_value(uri_json);
+            ad->container   = strdup(uri ? uri : "");
+            ad->auth        = AUTH_DENY;
+            if (auth_json) {
+              const char *auth = json_string_value(auth_json);
+              if (!auth) {
+                auth = "";
+              }
+              if (!strcmp(auth, "allow")) {
+                ad->auth = AUTH_ALLOW;
+              } else if (!strcmp(auth, "deny")) {
+                ad->auth = AUTH_DENY;
+              } else {
+                PluginError("auth_directive has unknown auth parameter '%s', defaulting to deny: %s", auth, uri);
+              }
+            } else {
+              PluginError("auth_directive is missing auth parameter, defaulting to deny: %s", uri);
+            }
+            PluginDebug("Adding auth_directive %d for %s.", (int)ad->auth, ad->container);
+          }
+        }
+        ad->container = NULL;
+      }
+    } else {
+      PluginDebug("No auth_directives to load for %s.", *issuer);
+    }
+
     json_t *key_ary = json_object_get(jwks, "keys");
     if (!key_ary) {
       PluginError("Failed to get keys member from jwk for issuer %s", *issuer);
@@ -250,3 +322,23 @@ config_signer(struct config *cfg)
   }
   return &cfg->signer;
 }
+
+bool
+uri_matches_auth_directive(struct config *cfg, const char *uri, size_t uri_ct)
+{
+  if (!cfg || !cfg->auth_directives || !uri) {
+    return false;
+  }
+
+  char *uri_s = malloc(uri_ct + 1);
+  memcpy(uri_s, uri, uri_ct);
+  uri_s[uri_ct] = 0;
+  for (const struct auth_directive *ad = cfg->auth_directives; ad->container; ++ad) {
+    if (jwt_check_uri(ad->container, uri_s)) {
+      free(uri_s);
+      return (ad->auth == AUTH_ALLOW);
+    }
+  }
+  free(uri_s);
+  return false;
+}
diff --git a/plugins/experimental/uri_signing/config.h b/plugins/experimental/uri_signing/config.h
index cfefcfa..75a82f2 100644
--- a/plugins/experimental/uri_signing/config.h
+++ b/plugins/experimental/uri_signing/config.h
@@ -16,6 +16,9 @@
  * limitations under the License.
  */
 
+#include <stdbool.h>
+#include <stdlib.h>
+
 struct config;
 struct _cjose_jwk_int;
 struct signer {
@@ -29,3 +32,4 @@ void config_delete(struct config *g);
 struct signer *config_signer(struct config *);
 struct _cjose_jwk_int **find_keys(struct config *cfg, const char *issuer);
 struct _cjose_jwk_int *find_key_by_kid(struct config *cfg, const char *issuer, const char *kid);
+bool uri_matches_auth_directive(struct config *cfg, const char *uri, size_t uri_ct);
diff --git a/plugins/experimental/uri_signing/jwt.c b/plugins/experimental/uri_signing/jwt.c
index e34a7a1..d509659 100644
--- a/plugins/experimental/uri_signing/jwt.c
+++ b/plugins/experimental/uri_signing/jwt.c
@@ -145,17 +145,17 @@ jwt_validate(struct jwt *jwt)
 }
 
 bool
-jwt_check_uri(struct jwt *jwt, const char *uri)
+jwt_check_uri(const char *sub, const char *uri)
 {
   static const char CONT_URI_STR[]         = "uri";
   static const char CONT_URI_PATTERN_STR[] = "uri-pattern";
   static const char CONT_URI_REGEX_STR[]   = "uri-regex";
 
-  if (!jwt || !uri) {
+  if (!sub || !*sub || !uri) {
     return false;
   }
 
-  const char *kind = jwt->sub, *container = jwt->sub;
+  const char *kind = sub, *container = sub;
   while (*container && *container != ':') {
     ++container;
   }
diff --git a/plugins/experimental/uri_signing/jwt.h b/plugins/experimental/uri_signing/jwt.h
index bfe1f5f..786e6b9 100644
--- a/plugins/experimental/uri_signing/jwt.h
+++ b/plugins/experimental/uri_signing/jwt.h
@@ -35,7 +35,7 @@ struct jwt {
 struct jwt *parse_jwt(json_t *raw);
 void jwt_delete(struct jwt *jwt);
 bool jwt_validate(struct jwt *jwt);
-bool jwt_check_uri(struct jwt *jwt, const char *uri);
+bool jwt_check_uri(const char *sub, 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);
diff --git a/plugins/experimental/uri_signing/parse.c b/plugins/experimental/uri_signing/parse.c
index cd8d7e8..ade5706 100644
--- a/plugins/experimental/uri_signing/parse.c
+++ b/plugins/experimental/uri_signing/parse.c
@@ -184,7 +184,7 @@ validate_jws(cjose_jws_t *jws, struct config *cfg, const char *uri, size_t uri_c
     }
   }
 
-  if (!jwt_check_uri(jwt, uri)) {
+  if (!jwt_check_uri(jwt->sub, uri)) {
     PluginDebug("Valid key for %16p that does not match uri.", jws);
     goto jwt_fail;
   }
diff --git a/plugins/experimental/uri_signing/uri_signing.c b/plugins/experimental/uri_signing/uri_signing.c
index c2062a1..0d81541 100644
--- a/plugins/experimental/uri_signing/uri_signing.c
+++ b/plugins/experimental/uri_signing/uri_signing.c
@@ -153,10 +153,10 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
   const int max_cpi       = 20;
   int64_t checkpoints[20] = {0};
   int cpi                 = 0;
+  int url_ct              = 0;
+  const char *url         = NULL;
 
   const char *package = "URISigningPackage";
-  int url_ct          = 0;
-  const char *url     = NULL;
 
   TSMBuffer mbuf;
   TSMLoc ul;
@@ -246,6 +246,9 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
   PluginDebug("Spent %" PRId64 " ns uri_signing verification of %.*s.", mark_timer(&t), url_ct, url);
   return TSREMAP_NO_REMAP;
 fail:
+  if (uri_matches_auth_directive((struct config *)ih, url, url_ct)) {
+    return TSREMAP_NO_REMAP;
+  }
   PluginDebug("Invalid JWT for %.*s", url_ct, url);
   TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_FORBIDDEN);
   PluginDebug("Spent %" PRId64 " ns uri_signing verification of %.*s.", mark_timer(&t), url_ct, url);

-- 
To stop receiving notification emails like this one, please contact
sorber@apache.org.