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, ¤t_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, ¤t_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.