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 <[email protected]> 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 [email protected].
