You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2018/03/12 17:55:06 UTC

[trafficserver] 04/08: Add path param url signing option and client ipv6 verification to url_sig.

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

zwoop pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit d621a8d135cf176dc270a22aef8b6fd70940fa42
Author: jrushford <jr...@apache.org>
AuthorDate: Mon Dec 11 21:45:34 2017 +0000

    Add path param url signing option and client ipv6 verification to url_sig.
    
    fix missing single quote in url_sig plugin documentation and double free.
    
    (cherry picked from commit ed479e26e2378b5f0ae5c278dcb3b3d25f0976fa)
---
 doc/admin-guide/plugins/url_sig.en.rst |  15 +-
 plugins/experimental/url_sig/README    |  57 +++++--
 plugins/experimental/url_sig/sign.pl   | 167 ++++++++++++++++++---
 plugins/experimental/url_sig/url_sig.c | 261 +++++++++++++++++++++++++++------
 plugins/experimental/url_sig/url_sig.h |   1 +
 5 files changed, 417 insertions(+), 84 deletions(-)

diff --git a/doc/admin-guide/plugins/url_sig.en.rst b/doc/admin-guide/plugins/url_sig.en.rst
index caee1cf..29b2314 100644
--- a/doc/admin-guide/plugins/url_sig.en.rst
+++ b/doc/admin-guide/plugins/url_sig.en.rst
@@ -152,7 +152,7 @@ will hand back to the client for redirection.
 
 Client IP
     The IP address of the client being redirected. This must be their IP as it
-    will appear to your |TS| cache::
+    will appear to your |TS| cache.  Both IP v4 and v6 addresses are supported::
 
         C=<ip address>
 
@@ -242,6 +242,19 @@ Signature
     portal, refer to the file ``sign.pl`` included with the source code of this
     plugin.
 
+Signature query parameters embedded in the URL path.
+
+    Optionally signature query parameters may be embedded in an opaque base64 encoded container
+    embedded in the URL path.  The format is  a semicolon, siganchor string, base64 encoded 
+    string.  ``url_sig`` automatically detects the use of embedded path parameters. The 
+    following example shows how to generate an embedded path parameters with ``sign.pl``::
+
+      ./sign.pl --url "http://test-remap.domain.com/vod/t/prog_index.m3u8?appid=2&t=1" --useparts 1 \
+      --algorithm 1 --duration 86400 --key kSCE1_uBREdGI3TPnr_dXKc9f_J4ZV2f --pathparams --siganchor urlsig
+
+      curl -s -o /dev/null -v --max-redirs 0 'http://test-remap.domain.com/vod/t;urlsig=O0U9MTQ2MzkyOTM4NTtBPTE7Sz0zO1A9MTtTPTIxYzk2YWRiZWZk'
+
+
 Edge Cache Debugging
 ====================
 
diff --git a/plugins/experimental/url_sig/README b/plugins/experimental/url_sig/README
index b5d8792..12b4929 100644
--- a/plugins/experimental/url_sig/README
+++ b/plugins/experimental/url_sig/README
@@ -37,18 +37,9 @@ Edge cache debugging
 		CONFIG proxy.config.diags.debug.enabled INT 1
 		CONFIG proxy.config.diags.debug.tags STRING url_sig
 
-	and do a traffic_ctl config reload; Debug output will go
-	to traffic.out.  Failed transactions (signature check fails
-	that is) will be logged in to error.log.
-
-Application Query Parameters.
-  If a request to be signed has application query parameters, the signing
-  parameters must be concatenated to the end of the requests application
-  query parameters.  The application query parameters will be included in
-  the signing calculation as determined by the 'Parts' signing explained
-  below.  At the edge after verification of the signing by this plugin,
-  the signing parameters are removed and the application query parameters
-  are preserved in the request.
+	and do a traffic_ctl config reload; Debug output will go to traffic.out.
+	Failed transactions (signature check fails that is) will be logged
+	in to error.log.
 
 Signing a URL
 	At the signing portal take the full URL, without any query string, and
@@ -85,6 +76,31 @@ Signing a URL
 		including "S=".
 		S=<signature>
 
+Signing a URL using path parameters instead of using a query string.
+
+  The parameters above may be embedded in the path part of the request
+  url vs using a query string.  When this method is used, the parameters
+  above are inserted after the path but before the file part of the path
+  in the request url.  Any origin application query parameters then follow
+  the file part of the request and are never part of the sign string.
+
+  Path parameters are separated by a ';' in the path.  The complete signature
+  string is base64 encoded as a single path parameter that is assinged to the 
+  'siganchor', and will appear in that path as siganchor=base64string. The 
+  following is an example signed request using the path parameter method and 
+  with an origin application query string:
+
+  http://ds-01.comcast.net/vod/t;urlsig=O0U9MTQ2MzkyOTYxODtBPTE7Sz0zO1A9MTtTPTEyZDlmN2RiNjUyZWI0YmI4MWYyNmVlMjE3MzczZGE5Y2VkYTRmZGY/Frag10Num10.ts?appid=2&t=1
+
+  Note that the signing string is embedded in the path between the last
+  directory part and before the file part of the request.  Using 'parts' in
+  sign.pl, the signature may be signed accordingly up to S= in the above
+  request. To generate a signed url using this method, use the --pathparams
+  option in sign.pl
+
+
+
+
 
 Example
 	Build, install
@@ -119,7 +135,7 @@ Example
 
 		map http://test-remap.domain.com http://google.com @plugin=url_sig.so @pparam=sign_test.config
 
-	Restart traffic server or "traffic_ctl config reload" - verify there are no errors in diags.log
+	Restart traffic server or traffic_ctl config reload  - verify there are no errors in diags.log
 
 	Try the path unsigned:
 
@@ -151,7 +167,8 @@ Example
 		Authorization Denied$
 		$
 
-	Sign the URL and try it again. Run the script with appropriate params, and it will output the curl line to run:
+	Sign the URL and try it again. Run the script with appropriate params, and
+	it will output the curl line to run:
 
 		$ ./sign.pl --url http://test-remap.domain.com/ --useparts 1 --algorithm 1 --duration 60 --keyindex 3 --key DTV4Tcn046eM9BzJMeYrYpm3kbqOtBs7
 		curl -s -o /dev/null -v --max-redirs 0 'http://test-remap.domain.com/?E=1397603088&A=1&K=3&P=1&S=28d822f68ac7265db61a8441e0877a98fe1007cc'
@@ -190,3 +207,15 @@ Example
 		{ [data not shown]
 		* Connection #0 to host localhost left intact
 		$
+
+Generating a signed URL with path parameters:
+
+  $ ./sign.pl --url "http://test-remap.domain.com/vod/t/prog_index.m3u8?appid=2&t=1" --useparts 1 --algorithm 1 --duration 86400 --keyindex 3 --key kSCE1_uBREdGI3TPnr_dXKc9f_J4ZV2f --pathparams --siganchor urlsig
+
+  curl -s -o /dev/null -v --max-redirs 0 'http://test-remap.domain.com/vod/t;urlsig=O0U9MTQ2MzkyOTM4NTtBPTE7Sz0zO1A9MTtTPTIxYzk2YWRiZWZkOGJkMDFhYmM3MmZkMTEzMWVkMGM5ZmU1ZmFiMjE/prog_index.m3u8?appid=2&t=1'
+
+
+Client IP in the Signing Script
+	Below is an example of how to include client ip in the signing script
+	Works for both IPv4 and IPv6
+	--client 10.10.10.10
diff --git a/plugins/experimental/url_sig/sign.pl b/plugins/experimental/url_sig/sign.pl
index 66fa729..6de4cc6 100755
--- a/plugins/experimental/url_sig/sign.pl
+++ b/plugins/experimental/url_sig/sign.pl
@@ -19,6 +19,7 @@
 use Digest::SHA qw(hmac_sha1 hmac_sha1_hex);
 use Digest::HMAC_MD5 qw(hmac_md5 hmac_md5_hex);
 use Getopt::Long;
+use MIME::Base64::URLSafe ();
 use strict;
 use warnings;
 my $key       = undef;
@@ -31,6 +32,10 @@ my $verbose   = 0;
 my $url       = undef;
 my $client    = undef;
 my $algorithm = 1;
+my $pathparams = 0;
+my $sig_anchor = undef;
+my $proxy = undef;
+my $scheme = "http://";
 
 $result = GetOptions(
 	"url=s"       => \$url,
@@ -40,13 +45,28 @@ $result = GetOptions(
 	"client=s"    => \$client,
 	"algorithm=i" => \$algorithm,
 	"keyindex=i"  => \$keyindex,
-	"verbose"     => \$verbose
+	"verbose"     => \$verbose,
+	"pathparams"  => \$pathparams,
+  "proxy=s"     => \$proxy,
+  "siganchor=s"  => \$sig_anchor
 );
 
 if ( !defined($key) || !defined($url) || !defined($duration) || !defined($keyindex) ) {
 	&help();
 	exit(1);
 }
+if ( defined($proxy) ) {
+  if ($proxy  !~ /http\:\/\/.*\:\d\d/) {
+    &help();
+  }
+}
+
+if ($url =~ m/^https/) {
+  $url =~ s/^https:\/\///;
+  $scheme = "https://";
+} else {
+  $url =~ s/^http:\/\///;
+}
 
 my $url_prefix = $url;
 $url_prefix =~ s/^([^:]*:\/\/).*$/$1/;
@@ -55,7 +75,36 @@ my $i              = 0;
 my $part_active    = 0;
 my $j              = 0;
 my @inactive_parts = ();
-foreach my $part ( split( /\//, $url ) ) {
+
+my $query_params = undef;
+my $urlHasParams = index($url,"?");
+my $file = undef;
+
+my @parts = (split(/\//, $url));
+my $parts_size = scalar(@parts);
+
+if ($pathparams) {
+  if (scalar(@parts) > 1) {
+    $file = pop @parts;
+  } else {
+    print STDERR "\nERROR: No file segment in the path when using --pathparams.\n\n";
+    &help();
+    exit 1;
+  }
+  if($urlHasParams) {
+    $file = (split(/\?/, $file))[0];
+  }
+  $parts_size = scalar(@parts);
+}
+if ($urlHasParams > 0) {
+  if ( ! $pathparams) {
+    ($parts[$parts_size -1], $query_params) = (split(/\?/, $parts[$parts_size - 1]));
+  } else {
+    $query_params = (split(/\?/, $url))[1];
+  }
+}
+
+foreach my $part (@parts) {
 	if ( length($useparts) > $i ) {
 		$part_active = substr( $useparts, $i++, 1 );
 	}
@@ -67,28 +116,42 @@ foreach my $part ( split( /\//, $url ) ) {
 	}
 	$j++;
 }
-my $urlHasParams = index($string,"?");
+
+my $signing_signature = undef;
 
 chop($string);
-if ( defined($client) ) {
-  if ($urlHasParams > 0) {
-	  $string .= "&C=" . $client . "&E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+if ($pathparams) {
+  if ( defined($client) ) {
+    $signing_signature = ";C=" . $client . ";E=" . ( time() + $duration ) . ";A=" . $algorithm . ";K=" . $keyindex . ";P=" . $useparts . ";S=";
+    $string .= $signing_signature;
   }
   else {
-	  $string .= "?C=" . $client . "&E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+    $signing_signature = ";E=" . ( time() + $duration ) . ";A=" . $algorithm . ";K=" . $keyindex . ";P=" . $useparts . ";S=";
+    $string .= $signing_signature;
   }
-}
-else {
-  if ($urlHasParams > 0) {
-	  $string .= "&E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+} else {
+  if ( defined($client) ) {
+    if ($urlHasParams > 0) {
+	    $signing_signature = "?$query_params" . "&C=" . $client . "&E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+      $string .= $signing_signature;
+    }
+    else {
+	    $signing_signature = "?C=" . $client . "&E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+      $string .= $signing_signature;
+    }
   }
   else {
-	  $string .= "?E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+    if ($urlHasParams > 0) {
+	    $signing_signature = "?$query_params" . "&E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+      $string .= $signing_signature;
+    }
+    else {
+	    $signing_signature = "?E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
+      $string .= $signing_signature;
+    }
   }
 }
 
-$verbose && print "signed string = " . $string . "\n";
-
 my $digest;
 if ( $algorithm == 1 ) {
 	$digest = hmac_sha1_hex( $string, $key );
@@ -96,16 +159,74 @@ if ( $algorithm == 1 ) {
 else {
 	$digest = hmac_md5_hex( $string, $key );
 }
-if ($urlHasParams == -1) {
-  my $qstring = ( split( /\?/, $string ) )[1];
 
-  print "curl -s -o /dev/null -v --max-redirs 0 '" . $url_prefix . $url . "?" . $qstring . $digest . "'\n";
-}
-else {
-  my $url_noparams = ( split( /\?/, $url ) )[0];
-  my $qstring = ( split( /\?/, $string ) )[1];
+$verbose && print "\nSigned String: $string\n\n";
+$verbose && print "\nUrl: $url\n";
+$verbose && print "\nsigning_signature: $signing_signature\n";
+$verbose && print "\ndigest: $digest\n";
 
-  print "curl -s -o /dev/null -v --max-redirs 0 '" . $url_prefix . $url_noparams . "?" . $qstring . $digest . "'\n";
+if ($urlHasParams == -1) { # no application query parameters.
+    if ( ! defined($proxy)) {
+      if ( ! $pathparams) {
+        print "curl -s -o /dev/null -v --max-redirs 0 '$scheme" . $url . $signing_signature . $digest . "'\n\n";
+      } else {
+        my $index = rindex($url, '/');
+        $url = substr($url,0,$index);
+        my $encoded = MIME::Base64::URLSafe::encode($signing_signature . $digest);
+        if (defined($sig_anchor)) {
+          print "curl -s -o /dev/null -v --max-redirs 0 '$scheme" . $url . ";${sig_anchor}=" . $encoded . "/$file" . "'\n\n";
+        } else {
+          print "curl -s -o /dev/null -v --max-redirs 0 '$scheme" . $url . "/" . $encoded . "/$file" . "'\n\n";
+        }
+      }
+    } else {
+      if ( ! $pathparams) {
+        print "curl -s -o /dev/null -v --max-redirs 0 --proxy $proxy '$scheme" . $url . $signing_signature . $digest .
+          "'\n\n";
+      } else {
+        my $index = rindex($url, '/');
+        $url = substr($url,0,$index);
+        my $encoded = MIME::Base64::URLSafe::encode($signing_signature . $digest);
+        if (defined($sig_anchor)) {
+          print "curl -s -o /dev/null -v --max-redirs 0 --proxy $proxy '$scheme" . $url . ";${sig_anchor}=" . $encoded .  "/$file" . "'\n\n";
+        } else {
+          print "curl -s -o /dev/null -v --max-redirs 0 --proxy $proxy '$scheme" . $url . "/" . $encoded .  "/$file" . "'\n\n";
+        }
+      }
+    }
+} else { # has application parameters.
+    $url = (split(/\?/, $url))[0];
+    if ( ! defined($proxy)) {
+      if ( ! $pathparams) {
+        print "curl -s -o /dev/null -v --max-redirs 0 '$scheme" . $url . $signing_signature . $digest . "'\n\n";
+      } else {
+        my $index = rindex($url, '/');
+        $url = substr($url,0,$index);
+        my $encoded = MIME::Base64::URLSafe::encode($signing_signature . $digest);
+        if (defined($sig_anchor)) {
+          print "curl -s -o /dev/null -v --max-redirs 0 '$scheme" . $url . ";${sig_anchor}=" . $encoded  . "/" . $file . "?$query_params"
+          . "'\n\n";
+        } else {
+          print "curl -s -o /dev/null -v --max-redirs 0 '$scheme" . $url . "/" . $encoded  . "/" . $file . "?$query_params"
+          . "'\n\n";
+        }
+      }
+    } else {
+      if ( ! $pathparams) {
+        print "curl -s -o /dev/null -v --max-redirs 0 --proxy $proxy '$scheme" . $url . $signing_signature . $digest .
+         "'\n\n";
+      } else {
+        my $index = rindex($url, '/');
+        $url = substr($url,0,$index);
+        my $encoded = MIME::Base64::URLSafe::encode($signing_signature . $digest);
+        if (defined($sig_anchor)) {
+          print "curl -s -o /dev/null -v --max-redirs 0 --proxy $proxy '$scheme" . $url . ";${sig_anchor}=" . $encoded  . "/" . $file . "?$query_params"
+          . "'\n\n";
+        } else {
+          print "curl -s -o /dev/null -v --max-redirs 0 --proxy $proxy '$scheme" . $url . "/" . $encoded .  "/$file?$query_params" . "'\n\n";
+        }
+      }
+    }
 }
 
 sub help {
@@ -119,5 +240,7 @@ sub help {
 	print "             [--client <value>] \\ \n";
 	print "             --key <value>  \\ \n";
 	print "             [--verbose] \n";
+	print "             [--pathparams] \n";
+	print "             [--proxy <url:port value>] ex value: http://myproxy:80\n";
 	print "\n";
 }
diff --git a/plugins/experimental/url_sig/url_sig.c b/plugins/experimental/url_sig/url_sig.c
index da9be83..cd4ebeb 100644
--- a/plugins/experimental/url_sig/url_sig.c
+++ b/plugins/experimental/url_sig/url_sig.c
@@ -39,6 +39,7 @@
 #include <limits.h>
 #include <ctype.h>
 #include <stdint.h>
+#include <stdbool.h>
 
 #ifdef HAVE_PCRE_PCRE_H
 #include <pcre/pcre.h>
@@ -58,6 +59,7 @@ struct config {
   pcre *regex;
   pcre_extra *regex_extra;
   int pristine_url_flag;
+  char *sig_anchor;
 };
 
 static void
@@ -65,6 +67,7 @@ free_cfg(struct config *cfg)
 {
   TSError("[url_sig] Cleaning up...");
   TSfree(cfg->err_url);
+  TSfree(cfg->sig_anchor);
 
   if (cfg->regex_extra) {
 #ifndef PCRE_STUDY_JIT_COMPILE
@@ -193,6 +196,8 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s
       } else {
         cfg->err_url = NULL;
       }
+    } else if (strncmp(line, "sig_anchor", 10) == 0) {
+      cfg->sig_anchor = TSstrndup(value, strlen(value));
     } else if (strncmp(line, "excl_regex", 10) == 0) {
       // compile and study regex
       const char *errptr;
@@ -326,6 +331,129 @@ getAppQueryString(const char *query_string, int query_length)
   }
 }
 
+static char *
+urlParse(char *url, char *anchor, char *new_path_seg, int new_path_seg_len, char *signed_seg, unsigned int signed_seg_len)
+{
+  char *segment[MAX_SEGMENTS];
+  unsigned char decoded_string[2048] = {'\0'};
+  char new_url[8192]                 = {'\0'};
+  char *p = NULL, *sig_anchor = NULL, *saveptr = NULL;
+  int i = 0, numtoks = 0, cp_len = 0, l, decoded_len = 0, sig_anchor_seg = 0;
+
+  char *skip = strchr(url, ':');
+  if (!skip || skip[1] != '/' || skip[2] != '/') {
+    return NULL;
+  }
+  skip += 3;
+  // preserve the scheme in the new_url.
+  strncat(new_url, url, skip - url);
+  TSDebug(PLUGIN_NAME, "%s:%d - new_url: %s\n", __FILE__, __LINE__, new_url);
+
+  // parse the url.
+  if ((p = strtok_r(skip, "/", &saveptr)) != NULL) {
+    segment[numtoks++] = p;
+    do {
+      p = strtok_r(NULL, "/", &saveptr);
+      if (p != NULL) {
+        segment[numtoks] = p;
+        if (anchor != NULL && sig_anchor_seg == 0) {
+          // look for the signed anchor string.
+          if ((sig_anchor = strcasestr(segment[numtoks], anchor)) != NULL) {
+            // null terminate this segment just before he signing anchor, this should be a ';'.
+            *(sig_anchor - 1) = '\0';
+            if ((sig_anchor = strstr(sig_anchor, "=")) != NULL) {
+              *sig_anchor = '\0';
+              sig_anchor++;
+              sig_anchor_seg = numtoks;
+            }
+          }
+        }
+        numtoks++;
+      }
+    } while (p != NULL && numtoks < MAX_SEGMENTS);
+  } else {
+    return NULL;
+  }
+  if ((numtoks >= MAX_SEGMENTS) || (numtoks < 3)) {
+    return NULL;
+  }
+
+  // create a new path string for later use when dealing with query parameters.
+  // this string will not contain the signing parameters.  skips the fqdn by
+  // starting with segment 1.
+  for (i = 1; i < numtoks; i++) {
+    // if no signing anchor is found, skip the signed parameters segment.
+    if (sig_anchor == NULL && i == numtoks - 2) {
+      // the signing parameters when no signature anchor is found, should be in the
+      // last path segment so skip them.
+      continue;
+    }
+    l = strlen(segment[i]);
+    if (l + 1 > new_path_seg_len) {
+      TSError("insuficient space to copy into new_path_seg buffer.");
+      return NULL;
+    } else {
+      strncat(new_path_seg, segment[i], l);
+      if (i != numtoks - 1) {
+        strncat(new_path_seg, "/", 1);
+      }
+      cp_len += l + 1;
+    }
+  }
+  TSDebug(PLUGIN_NAME, "new_path_seg: %s", new_path_seg);
+
+  // save the encoded signing parameter data
+  if (sig_anchor != NULL) { // a signature anchor string was found.
+    if (strlen(sig_anchor) < signed_seg_len) {
+      strncpy(signed_seg, sig_anchor, strlen(sig_anchor));
+    } else {
+      TSError("insuficient space to copy into new_path_seg buffer.");
+    }
+  } else { // no signature anchor string was found, assum it is in the last path segment.
+    if (strlen(segment[numtoks - 2]) < signed_seg_len) {
+      strncpy(signed_seg, segment[numtoks - 2], strlen(segment[numtoks - 2]));
+    } else {
+      TSError("insuficient space to copy into new_path_seg buffer.");
+      return NULL;
+    }
+  }
+  TSDebug(PLUGIN_NAME, "signed_seg: %s", signed_seg);
+
+  // no signature anchor was found so decode and save the signing parameters assumed
+  // to be in the last path segment.
+  if (sig_anchor == NULL) {
+    if (TSBase64Decode(segment[numtoks - 2], strlen(segment[numtoks - 2]), decoded_string, sizeof(decoded_string),
+                       (size_t *)&decoded_len) != TS_SUCCESS) {
+      TSDebug(PLUGIN_NAME, "Unable to decode the  path parameter string.");
+    }
+  } else {
+    if (TSBase64Decode(sig_anchor, strlen(sig_anchor), decoded_string, sizeof(decoded_string), (size_t *)&decoded_len) !=
+        TS_SUCCESS) {
+      TSDebug(PLUGIN_NAME, "Unable to decode the  path parameter string.");
+    }
+  }
+  TSDebug(PLUGIN_NAME, "decoded_string: %s", decoded_string);
+
+  for (i = 0; i < numtoks; i++) {
+    // cp the base64 decoded string.
+    if (i == sig_anchor_seg && sig_anchor != NULL) {
+      strncat(new_url, segment[i], strlen(segment[i]));
+      strncat(new_url, (char *)decoded_string, strlen((char *)decoded_string));
+      strncat(new_url, "/", 1);
+      continue;
+    } else if (i == numtoks - 2 && sig_anchor == NULL) {
+      strncat(new_url, (char *)decoded_string, strlen((char *)decoded_string));
+      strncat(new_url, "/", 1);
+      continue;
+    }
+    strncat(new_url, segment[i], strlen(segment[i]));
+    if (i < numtoks - 1) {
+      strncat(new_url, "/", 1);
+    }
+  }
+  return TSstrndup(new_url, strlen(new_url));
+}
+
 TSRemapStatus
 TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
 {
@@ -341,23 +469,21 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
   unsigned int i       = 0;
   int j                = 0;
   unsigned int sig_len = 0;
+  bool has_path_params = false;
 
   /* all strings are locally allocated except url... about 25k per instance */
-  char *const current_url    = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &current_url_len);
-  const char *url            = current_url;
-  char signed_part[8192]     = {'\0'}; // this initializes the whole array and is needed
-  char urltokstr[8192]       = {'\0'};
-  char client_ip[CIP_STRLEN] = {'\0'};
-  char ipstr[CIP_STRLEN]     = {'\0'};
+  char *const current_url = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &current_url_len);
+  char *url               = current_url;
+  char path_params[8192] = {'\0'}, new_path[8192] = {'\0'};
+  char signed_part[8192]           = {'\0'}; // this initializes the whole array and is needed
+  char urltokstr[8192]             = {'\0'};
+  char client_ip[INET6_ADDRSTRLEN] = {'\0'}; // chose the larger ipv6 size
+  char ipstr[INET6_ADDRSTRLEN]     = {'\0'}; // chose the larger ipv6 size
   unsigned char sig[MAX_SIG_SIZE + 1];
   char sig_string[2 * MAX_SIG_SIZE + 1];
 
-  int retval, sockfd;
-  socklen_t peer_len;
-  struct sockaddr_in peer;
-
   if (current_url_len >= MAX_REQ_LEN - 1) {
-    err_log(current_url, "URL string too long.");
+    err_log(url, "URL string too long");
     goto deny;
   }
 
@@ -400,49 +526,80 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     }
   }
 
-  if (query == NULL) {
-    err_log(url, "Has no query string.");
-    goto deny;
+  // check for path params.
+  if (query == NULL || strstr(query, "E=") == NULL) {
+    if ((url = urlParse(url, cfg->sig_anchor, new_path, 8192, path_params, 8192)) == NULL) {
+      err_log(url, "Has no signing query string or signing path parameters.");
+      goto deny;
+    }
+    has_path_params = true;
+    query           = strstr(url, ";");
+
+    if (query == NULL) {
+      err_log(url, "Has no signing query string or signing path parameters.");
+      goto deny;
+    }
   }
 
   /* first, parse the query string */
-  query++; /* get rid of the ? */
+  if (!has_path_params) {
+    query++; /* get rid of the ? */
+  }
   TSDebug(PLUGIN_NAME, "Query string is:%s", query);
 
   // Client IP - this one is optional
   const char *cp = strstr(query, CIP_QSTRING "=");
+  const char *pp = NULL;
   if (cp != NULL) {
-    int len_cip_qstring = strlen(CIP_QSTRING);
-    cp += len_cip_qstring - 1;
-    const char *cpp = strchr(cp, '&');
-    if (!cpp) {
-      err_log(url, "All required values after IP address string missing");
-      goto deny;
-    }
-    if (!cpp || (cpp - cp) > CIP_STRLEN - 1 || (cpp - cp) < 4) {
-      err_log(url, "IP address string too long or short");
-      goto deny;
-    }
-    memcpy(client_ip, cp + len_cip_qstring + 1, (cpp - cp - (len_cip_qstring + 1)));
-    client_ip[cpp - cp - (len_cip_qstring + 1)] = '\0';
-    TSDebug(PLUGIN_NAME, "CIP: -%s-", client_ip);
-    retval = TSHttpTxnClientFdGet(txnp, &sockfd);
-    if (retval != TS_SUCCESS) {
-      err_log(url, "Error getting sockfd.");
-      goto deny;
-    }
-    peer_len = sizeof(peer);
-    if (getpeername(sockfd, (struct sockaddr *)&peer, &peer_len) != 0) {
-      perror("Can't get peer address:");
-    }
-    struct sockaddr_in *s = (struct sockaddr_in *)&peer;
-    inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
-    TSDebug(PLUGIN_NAME, "Peer address: -%s-", ipstr);
-    if (strcmp(ipstr, client_ip) != 0) {
-      err_log(url, "Client IP doesn't match signature.");
+    cp += (strlen(CIP_QSTRING) + 1);
+    struct sockaddr const *ip = TSHttpTxnClientAddrGet(txnp);
+    if (ip == NULL) {
+      TSError("Can't get client ip address.");
       goto deny;
+    } else {
+      switch (ip->sa_family) {
+      case AF_INET:
+        TSDebug(PLUGIN_NAME, "ip->sa_family: AF_INET");
+        has_path_params == false ? (pp = strstr(cp, "&")) : (pp = strstr(cp, ";"));
+        if ((pp - cp) > INET_ADDRSTRLEN - 1 || (pp - cp) < 4) {
+          err_log(url, "IP address string too long or short.");
+          goto deny;
+        }
+        strncpy(client_ip, cp, (pp - cp));
+        client_ip[pp - cp] = '\0';
+        TSDebug(PLUGIN_NAME, "CIP: -%s-", client_ip);
+        inet_ntop(AF_INET, &(((struct sockaddr_in *)ip)->sin_addr), ipstr, sizeof ipstr);
+        TSDebug(PLUGIN_NAME, "Peer address: -%s-", ipstr);
+        if (strcmp(ipstr, client_ip) != 0) {
+          err_log(url, "Client IP doesn't match signature.");
+          goto deny;
+        }
+        break;
+      case AF_INET6:
+        TSDebug(PLUGIN_NAME, "ip->sa_family: AF_INET6");
+        has_path_params == false ? (pp = strstr(cp, "&")) : (pp = strstr(cp, ";"));
+        if ((pp - cp) > INET6_ADDRSTRLEN - 1 || (pp - cp) < 4) {
+          err_log(url, "IP address string too long or short.");
+          goto deny;
+        }
+        strncpy(client_ip, cp, (pp - cp));
+        client_ip[pp - cp] = '\0';
+        TSDebug(PLUGIN_NAME, "CIP: -%s-", client_ip);
+        inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)ip)->sin6_addr), ipstr, sizeof ipstr);
+        TSDebug(PLUGIN_NAME, "Peer address: -%s-", ipstr);
+        if (strcmp(ipstr, client_ip) != 0) {
+          err_log(url, "Client IP doesn't match signature.");
+          goto deny;
+        }
+        break;
+      default:
+        TSError("%s: Unknown address family %d", PLUGIN_NAME, ip->sa_family);
+        goto deny;
+        break;
+      }
     }
   }
+
   // Expiration
   cp = strstr(query, EXP_QSTRING "=");
   if (cp != NULL) {
@@ -487,7 +644,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
   if (cp != NULL) {
     cp += strlen(PAR_QSTRING) + 1;
     parts = cp; // NOTE parts is not NULL terminated it is terminated by "&" of next param
-    cp    = strchr(parts, '&');
+    has_path_params == false ? (cp = strstr(parts, "&")) : (cp = strstr(parts, ";"));
     if (cp) {
       TSDebug(PLUGIN_NAME, "Parts: %.*s", (int)(cp - parts), parts);
     } else {
@@ -518,7 +675,7 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
           keyindex, parts, signature);
 
   /* find the string that was signed - cycle through the parts letters, adding the part of the fqdn/path if it is 1 */
-  cp = strchr(url, '?');
+  has_path_params == false ? (cp = strchr(url, '?')) : (cp = strchr(url, ';'));
   // Skip scheme and initial forward slashes.
   const char *skip = strchr(url, ':');
   if (!skip || skip[1] != '/' || skip[2] != '/') {
@@ -540,8 +697,10 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri)
     part = strtok_r(NULL, "/", &strtok_r_p);
   }
 
-  signed_part[strlen(signed_part) - 1] = '?'; // chop off the last /, replace with '?'
-  cp                                   = strstr(query, SIG_QSTRING "=");
+  // chop off the last /, replace with '?' or ';' as appropriate.
+  has_path_params == false ? (signed_part[strlen(signed_part) - 1] = '?') : (signed_part[strlen(signed_part) - 1] = '\0');
+  cp = strstr(query, SIG_QSTRING "=");
+  TSDebug(PLUGIN_NAME, "cp: %s, query: %s, signed_part: %s", cp, query, signed_part);
   strncat(signed_part, query, (cp - query) + strlen(SIG_QSTRING) + 1);
 
   TSDebug(PLUGIN_NAME, "Signed string=\"%s\"", signed_part);
@@ -627,6 +786,13 @@ allow:
     current_query++;
     app_qry = getAppQueryString(current_query, strlen(current_query));
   }
+  TSDebug(PLUGIN_NAME, "has_path_params: %d", has_path_params);
+  if (has_path_params) {
+    if (*new_path) {
+      TSUrlPathSet(rri->requestBufp, rri->requestUrl, new_path, strlen(new_path));
+    }
+    TSUrlHttpParamsSet(rri->requestBufp, rri->requestUrl, NULL, 0);
+  }
 
   TSfree((void *)current_url);
 
@@ -640,5 +806,6 @@ allow:
   if (rval != TS_SUCCESS) {
     TSError("[url_sig] Error setting the query string: %d.", rval);
   }
+
   return TSREMAP_NO_REMAP;
 }
diff --git a/plugins/experimental/url_sig/url_sig.h b/plugins/experimental/url_sig/url_sig.h
index 46e8e72..d5fbb95 100644
--- a/plugins/experimental/url_sig/url_sig.h
+++ b/plugins/experimental/url_sig/url_sig.h
@@ -35,6 +35,7 @@
 #define EXP_STRLEN 16
 #define PAR_STRLEN 16
 #define MAX_PARTS 32
+#define MAX_SEGMENTS 64
 
 #define MAX_HTTP_REQUEST_SIZE 8192 //
 

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