On Apr 18, 2014, at 12:56 PM, [email protected] wrote:
> Repository: trafficserver
> Updated Branches:
> refs/heads/master bcfd36abf -> 88f18501d
>
>
> TS-2732: Add url_sig plugin
>
>
> Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
> Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/88f18501
> Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/88f18501
> Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/88f18501
>
> Branch: refs/heads/master
> Commit: 88f18501d110bdff6a7347f307d94eb3629dad79
> Parents: bcfd36a
> Author: Phil Sorber <[email protected]>
> Authored: Fri Apr 18 13:49:12 2014 -0600
> Committer: Phil Sorber <[email protected]>
> Committed: Fri Apr 18 13:55:42 2014 -0600
>
> ----------------------------------------------------------------------
> CHANGES | 2 +
> NOTICE | 4 +-
> configure.ac | 1 +
> plugins/experimental/Makefile.am | 1 +
> plugins/experimental/url_sig/Makefile.am | 21 ++
> plugins/experimental/url_sig/Makefile.tsxs | 26 ++
> plugins/experimental/url_sig/README | 183 ++++++++++
> plugins/experimental/url_sig/genkeys.pl | 29 ++
> plugins/experimental/url_sig/sign.pl | 101 ++++++
> plugins/experimental/url_sig/url_sig.c | 456 ++++++++++++++++++++++++
> plugins/experimental/url_sig/url_sig.h | 52 +++
> 11 files changed, 874 insertions(+), 2 deletions(-)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/CHANGES
> ----------------------------------------------------------------------
> diff --git a/CHANGES b/CHANGES
> index 49c2227..d30d399 100644
> --- a/CHANGES
> +++ b/CHANGES
> @@ -1,6 +1,8 @@
> -*- coding: utf-8 -*-
> Changes with Apache Traffic Server 5.0.0
>
> + *) [TS-2732] Add url_sign (Signed URL's) plugin.
> +
> *) [TS-2650] Redirect handling enhancements in ATS
>
> *) [TS-1125] POST's with Expect: 100-continue are slowed by delayed 100
> response
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/NOTICE
> ----------------------------------------------------------------------
> diff --git a/NOTICE b/NOTICE
> index 00196cb..dacd4a7 100644
> --- a/NOTICE
> +++ b/NOTICE
> @@ -54,8 +54,8 @@ Copyright (c) 2013 LinkedIn
>
> ~~~
>
> -remap_stats plugin developed by Comcast.
> -Copyright (C) 2013 Comcast
> +remap_stats and url_sig plugins developed by Comcast.
> +Copyright (C) 2014 Comcast
>
> ~~~
>
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/configure.ac
> ----------------------------------------------------------------------
> diff --git a/configure.ac b/configure.ac
> index bd3ddcf..7467a2d 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1946,6 +1946,7 @@ AC_CONFIG_FILES([
> plugins/experimental/s3_auth/Makefile
> plugins/experimental/spdy/Makefile
> plugins/experimental/ts_lua/Makefile
> + plugins/experimental/url_sig/Makefile
> plugins/experimental/xdebug/Makefile
> proxy/Makefile
> proxy/api/ts/Makefile
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/Makefile.am
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/Makefile.am
> b/plugins/experimental/Makefile.am
> index 4b811c1..b8bddce 100644
> --- a/plugins/experimental/Makefile.am
> +++ b/plugins/experimental/Makefile.am
> @@ -34,6 +34,7 @@ SUBDIRS = \
> s3_auth \
> spdy \
> ts_lua \
> + url_sig \
> xdebug
>
> endif
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/Makefile.am
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/Makefile.am
> b/plugins/experimental/url_sig/Makefile.am
> new file mode 100644
> index 0000000..a9011ff
> --- /dev/null
> +++ b/plugins/experimental/url_sig/Makefile.am
> @@ -0,0 +1,21 @@
> +# Licensed to the Apache Software Foundation (ASF) under one
> +# or more contributor license agreements. See the NOTICE file
> +# distributed with this work for additional information
> +# regarding copyright ownership. The ASF licenses this file
> +# to you under the Apache License, Version 2.0 (the
> +# "License"); you may not use this file except in compliance
> +# with the License. You may obtain a copy of the License at
> +#
> +# http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +include $(top_srcdir)/build/plugins.mk
> +
> +pkglib_LTLIBRARIES = url_sig.la
> +url_sig_la_SOURCES = url_sig.c
> +url_sig_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/Makefile.tsxs
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/Makefile.tsxs
> b/plugins/experimental/url_sig/Makefile.tsxs
> new file mode 100644
> index 0000000..e3ee96e
> --- /dev/null
> +++ b/plugins/experimental/url_sig/Makefile.tsxs
> @@ -0,0 +1,26 @@
> +# Licensed to the Apache Software Foundation (ASF) under one
> +# or more contributor license agreements. See the NOTICE file
> +# distributed with this work for additional information
> +# regarding copyright ownership. The ASF licenses this file
> +# to you under the Apache License, Version 2.0 (the
> +# "License"); you may not use this file except in compliance
> +# with the License. You may obtain a copy of the License at
> +#
> +# http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +TSXS?=tsxs
> +
> +all: url_sig.c
> + $(TSXS) -v -o url_sig.so $?
> +
> +install:
> + $(TSXS) -i -o url_sig.so
> +
> +clean:
> + rm -f *.lo *.so
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/README
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/README
> b/plugins/experimental/url_sig/README
> new file mode 100644
> index 0000000..d2a4ce0
> --- /dev/null
> +++ b/plugins/experimental/url_sig/README
Can you move this to the docs? It would be docs/reference/plugins/url_sig.rst
> @@ -0,0 +1,183 @@
> +Signed URL plugin
> +
> +This ATS plugin checks a signature query string on the URL, and rejects (HTTP
> +403), or redirects (HTTP 302) when this check fails. The signature is based
> on
> +a secret (key) that a signing portal and the Traffic Server cache share. The
> +algorithm for the signature can be MD5 or SHA1. When the check passes, the
> +query string is stripped, and the request is handled as if there was no query
> +string in the first place.
> +
> +This plugin comes with 2 example perl scripts. The sign.pl script shows how
> to
> +sign a URL using the different options, this script is just an example, in
> any
> +real usage scenario, the content owner will incorporate this functionality in
> +their portal. The genkeys.pl script is a quick hack to generate a config file
> +with random keys. Keep your keys to yourself (secret) if you are using this
> +plugin, and only share them with the content owner.
> +
> +Signed URLs do not replace DRM.
> +
> +Quick install:
> + You can build with ./configure --enable-experimental-plugins or
> + with tsxs:
> +
> + Make sure devel packages for traffic-server are installed.
> + Make sure that 'tsxs' is in your path.
> + Make sure versions of this plugin and ATS are compatible.
> +
> + make -f Makefile.tsxs
> + make -f Makefile.tsxs install
> +
> + add @plugin=url_sig.so @pparam=<config file> to the remap rule you want
> + to be signed
> +
> +
> +Edge cache debugging
> + To enable the TSDebug verbose logging, change records.config to have:
> +
> + CONFIG proxy.config.diags.debug.enabled INT 1
> + CONFIG proxy.config.diags.debug.tags STRING url_sig
> +
> + and do a traffic_line -x; 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
> + add on a query string with the following parameters:
> +
> + Client IP address
> + The client IP address that this signature is valid for.
> + C=<client IP address>
> + Expiration
> + The Expiration time (seconds since epoch) of this signature.
> + E=<expiration time in secs since unix epoch>
> + Algorithm
> + The Algorithm used to create the signature. Only 1 (HMAC_SHA1)
> + and 2 (HMAC_MD5) are supported at this time
> + A=<algorithm number>
> + Key index
> + Index of the key used. This is the index of the key in the
> + configuration file on the cache. The set of keys is a shared
> + secret between the signing portal and the edge caches. There
> + is one set of keys per reverse proxy domain (fqdn).
> + K=<key index used>
> + Parts
> + Parts to use for the signature, always excluding the scheme
> + (http://). parts0 = fqdn, parts1..x is the directory parts
> + of the path, if there are more parts to the path than letters
> + in the parts param, the last one is repeated for those.
> + Examples:
> + 1: use fqdn and all of URl path
> + 0110: use part1 and part 2 of path only
> + 01: use everything except the fqdn
> + P=<parts string (0's and 1's>
> + Signature
> + The signature over the parts + the query string up to and
> + including "S=".
> + S=<signature>
> +
> +
> +Example
> + Build, install
> +
> + make -f Makefile.tsxs
> + make -f Makefile.tsxs install
> +
> + Create the config file, using the genkeys script, or otherwise.
> +
> + $ ./genkeys.pl >
> /usr/local/trafficserver-master/etc/trafficserver/sign_test.config
> + $ cat
> /usr/local/trafficserver-master/etc/trafficserver/sign_test.config
> + key0 = YwG7iAxDo6Gaa38KJOceV4nsxiAJZ3DS
> + key1 = nLE3SZKRgaNM9hLz_HnIvrCw_GtTUJT1
> + key2 = YicZbmr6KlxfxPTJ3p9vYhARdPQ9WJYZ
> + key3 = DTV4Tcn046eM9BzJMeYrYpm3kbqOtBs7
> + key4 = C1r6R6MINoQd5YSH25fU66tuRhhz3fs_
> + key5 = l4dxe6YEpYbJtyiOmX5mafhwKImC5kej
> + key6 = ekKNHXu9_oOC5eqIGJVxV0vI9FYvKVya
> + key7 = BrjibTmpTTuhMHqphkQAuCWA0Zg97WQB
> + key8 = rEtWLb1jcYoq9VG8Z8TKgX4GxBuro20J
> + key9 = mrP_6ibDBG4iYpfDB6W8yn3ZyGmdwc6M
> + key10 = tbzoTTGZXPLcvpswCQCYz1DAIZcAOGyX
> + key11 = lWsn6gUeSEW79Fk2kwKVfzhVG87EXLna
> + key12 = Riox0SmGtBWsrieLUHVWtpj18STM4MP1
> + key13 = kBsn332B7yG3HdcV7Tw51pkvHod7_84l
> + key14 = hYI4GUoIlZRf0AyuXkT3QLvBMEoFxkma
> + key15 = EIgJKwIR0LU9CUeTUdVtjMgGmxeCIbdg
> + error_url = 403
If I'm reading the code right, the error_url key is
error_url = STATUS URL
Is that right?
> + $
> +
> + Add the remap rule like
> +
> + map http://test-remap.domain.com http://google.com
> @plugin=url_sig.so @pparam=sign_test.config
> +
> + Restart traffic server or traffic_line -x - verify there are no errors
> in diags.log
> +
> + Try the path unsigned:
> +
> + $ curl -vs -H'Host: test-remap.domain.com'
> http://localhost:8080/
> + * Adding handle: conn: 0x200f8a0
> + * Adding handle: send: 0
> + * Adding handle: recv: 0
> + * Curl_addHandleToPipeline: length: 1
> + * - Conn 0 (0x200f8a0) send_pipe: 1, recv_pipe: 0
> + * About to connect() to localhost port 8080 (#0)
> + * Trying 127.0.0.1...
> + * Connected to localhost (127.0.0.1) port 8080 (#0)
> + > GET / HTTP/1.1
> + > User-Agent: curl/7.32.0
> + > Accept: */*
> + > Host: test-remap.domain.com
> + >
> + < HTTP/1.1 403 Forbidden
> + < Date: Tue, 15 Apr 2014 22:57:32 GMT
> + < Connection: close
> + * Server ATS/5.0.0 is not blacklisted
> + < Server: ATS/5.0.0
> + < Cache-Control: no-store
> + < Content-Type: text/plain
> + < Content-Language: en
> + < Content-Length: 21
> + <
> + * Closing connection 0
> + 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.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'
> +
> + Since test-remap.domain.com doesn't actually exist, we have to change
> that line slightly:
> +
> + $ curl -s -o /dev/null -v --max-redirs 0 -H 'Host:
> test-remap.domain.com'
> 'http://localhost:8080/?E=1397603088&A=1&K=3&P=1&S=28d822f68ac7265db61a8441e0877a98fe1007cc'
> + * Adding handle: conn: 0xef0a90
> + * Adding handle: send: 0
> + * Adding handle: recv: 0
> + * Curl_addHandleToPipeline: length: 1
> + * - Conn 0 (0xef0a90) send_pipe: 1, recv_pipe: 0
> + * About to connect() to localhost port 8080 (#0)
> + * Trying 127.0.0.1...
> + * Connected to localhost (127.0.0.1) port 8080 (#0)
> + > GET
> /?E=1397603088&A=1&K=3&P=1&S=28d822f68ac7265db61a8441e0877a98fe1007cc HTTP/1.1
> + > User-Agent: curl/7.32.0
> + > Accept: */*
> + > Host: test-remap.domain.com
> + >
> + < HTTP/1.1 200 OK
> + < Location: http://www.google.com/
> + < Content-Type: text/html; charset=UTF-8
> + < Date: Tue, 15 Apr 2014 23:04:36 GMT
> + < Expires: Thu, 15 May 2014 23:04:36 GMT
> + < Cache-Control: public, max-age=2592000
> + * Server ATS/5.0.0 is not blacklisted
> + < Server: ATS/5.0.0
> + < Content-Length: 219
> + < X-XSS-Protection: 1; mode=block
> + < X-Frame-Options: SAMEORIGIN
> + < Alternate-Protocol: 80:quic
> + < Age: 0
> + < Connection: keep-alive
> + <
> + { [data not shown]
> + * Connection #0 to host localhost left intact
> + $
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/genkeys.pl
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/genkeys.pl
> b/plugins/experimental/url_sig/genkeys.pl
> new file mode 100755
> index 0000000..ae5bc07
> --- /dev/null
> +++ b/plugins/experimental/url_sig/genkeys.pl
> @@ -0,0 +1,29 @@
> +#!/usr/bin/perl
> +
> +# Licensed to the Apache Software Foundation (ASF) under one
> +# or more contributor license agreements. See the NOTICE file
> +# distributed with this work for additional information
> +# regarding copyright ownership. The ASF licenses this file
> +# to you under the Apache License, Version 2.0 (the
> +# "License"); you may not use this file except in compliance
> +# with the License. You may obtain a copy of the License at
> +#
> +# http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +my $len = 32;
> +my @chars = ( 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_' );
> +foreach my $i ( 0 .. 15 ) {
> + my $string = "";
> + foreach ( 1 .. $len ) {
> + $string .= $chars[ rand @chars ];
> + }
> + print "key" . $i . " = " . $string . "\n";
> +}
> +#print "error_url=302 http://www.domain.com/this/is/the/path/error.html\n";
> +print "error_url = 403\n";
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/sign.pl
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/sign.pl
> b/plugins/experimental/url_sig/sign.pl
> new file mode 100755
> index 0000000..d3fbdeb
> --- /dev/null
> +++ b/plugins/experimental/url_sig/sign.pl
> @@ -0,0 +1,101 @@
> +#!/usr/bin/perl
> +
> +# Licensed to the Apache Software Foundation (ASF) under one
> +# or more contributor license agreements. See the NOTICE file
> +# distributed with this work for additional information
> +# regarding copyright ownership. The ASF licenses this file
> +# to you under the Apache License, Version 2.0 (the
> +# "License"); you may not use this file except in compliance
> +# with the License. You may obtain a copy of the License at
> +#
> +# http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS,
> +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +# See the License for the specific language governing permissions and
> +# limitations under the License.
> +
> +use Digest::SHA qw(hmac_sha1 hmac_sha1_hex);
> +use Digest::HMAC_MD5 qw(hmac_md5 hmac_md5_hex);
> +use Getopt::Long;
> +use strict;
> +use warnings;
> +my $key = undef;
> +my $string = undef;
> +my $useparts = undef;
> +my $result = undef;
> +my $duration = undef;
> +my $keyindex = undef;
> +my $verbose = 0;
> +my $url = undef;
> +my $client = undef;
> +my $algorithm = 1;
> +
> +$result = GetOptions(
> + "url=s" => \$url,
> + "useparts=s" => \$useparts,
> + "duration=i" => \$duration,
> + "key=s" => \$key,
> + "client=s" => \$client,
> + "algorithm=i" => \$algorithm,
> + "keyindex=i" => \$keyindex,
> + "verbose" => \$verbose
> +);
> +
> +if ( !defined($key) || !defined($url) || !defined($duration) ||
> !defined($keyindex) ) {
> + &help();
> + exit(1);
> +}
> +
> +$url =~ s/^http:\/\///;
> +my $i = 0;
> +my $part_active = 0;
> +my $j = 0;
> +my @inactive_parts = ();
> +foreach my $part ( split( /\//, $url ) ) {
> + if ( length($useparts) > $i ) {
> + $part_active = substr( $useparts, $i++, 1 );
> + }
> + if ($part_active) {
> + $string .= $part . "/";
> + }
> + else {
> + $inactive_parts[$j] = $part;
> + }
> + $j++;
> +}
> +chop($string);
> +if ( defined($client) ) {
> + $string .= "?C=" . $client . "&E=" . ( time() + $duration ) . "&A=" .
> $algorithm . "&K=" . $keyindex . "&P=" . $useparts . "&S=";
> +}
> +else {
> + $string .= "?E=" . ( time() + $duration ) . "&A=" . $algorithm . "&K="
> . $keyindex . "&P=" . $useparts . "&S=";
> +}
> +
> +$verbose && print "signed string = " . $string . "\n";
> +
> +my $digest;
> +if ( $algorithm == 1 ) {
> + $digest = hmac_sha1_hex( $string, $key );
> +}
> +else {
> + $digest = hmac_md5_hex( $string, $key );
> +}
> +my $qstring = ( split( /\?/, $string ) )[1];
> +
> +print "curl -s -o /dev/null -v --max-redirs 0 'http://" . $url . "?" .
> $qstring . $digest . "'\n";
> +
> +sub help {
> + print "sign.pl - Example signing utility in perl for signed URLs\n";
> + print "Usage: \n";
> + print " ./sign.pl --url <value> \\ \n";
> + print " --useparts <value> \\ \n";
> + print " --algorithm <value> \\ \n";
> + print " --duration <value> \\ \n";
> + print " --keyindex <value> \\ \n";
> + print " [--client <value>] \\ \n";
> + print " --key <value> \\ \n";
> + print " [--verbose] \n";
> + print "\n";
> +}
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/url_sig.c
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/url_sig.c
> b/plugins/experimental/url_sig/url_sig.c
> new file mode 100644
> index 0000000..0cde9d3
> --- /dev/null
> +++ b/plugins/experimental/url_sig/url_sig.c
> @@ -0,0 +1,456 @@
> +/** @file
> + Licensed to the Apache Software Foundation (ASF) under one
> + or more contributor license agreements. See the NOTICE file
> + distributed with this work for additional information
> + regarding copyright ownership. The ASF licenses this file
> + to you under the Apache License, Version 2.0 (the
> + "License"); you may not use this file except in compliance
> + with the License. You may obtain a copy of the License at
> +
> + http://www.apache.org/licenses/LICENSE-2.0
> +
> + Unless required by applicable law or agreed to in writing, software
> + distributed under the License is distributed on an "AS IS" BASIS,
> + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + See the License for the specific language governing permissions and
> + limitations under the License.
> + */
> +
> +#include "url_sig.h"
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <time.h>
> +#include <openssl/hmac.h>
> +#include <openssl/evp.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <arpa/inet.h>
> +#include <limits.h>
> +#include <ctype.h>
> +
> +#include <ts/ts.h>
> +#include <ts/remap.h>
> +
> +static const char* PLUGIN_NAME = "url_sig";
> +
> +struct config {
> + char *map_from;
> + char *map_to;
> + TSHttpStatus err_status;
> + char *err_url;
> + char keys[MAX_KEY_NUM][MAX_KEY_LEN];
> +};
> +
> +TSReturnCode TSRemapInit(TSRemapInterface* api_info, char *errbuf, int
> errbuf_size) {
> + if (!api_info) {
> + strncpy(errbuf, "[tsremap_init] - Invalid TSRemapInterface
> argument", (size_t) (errbuf_size - 1));
> + return TS_ERROR;
> + }
> +
> + if (api_info->tsremap_version < TSREMAP_VERSION) {
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapInit] - Incorrect
> API version %ld.%ld", api_info->tsremap_version >> 16,
> + (api_info->tsremap_version & 0xffff));
> + return TS_ERROR;
> + }
> +
> + TSDebug(PLUGIN_NAME, "plugin is succesfully initialized");
> + return TS_SUCCESS;
> +}
> +
> +// To force a config file reload touch remap.config and do a "traffic_line
> -x"
> +TSReturnCode TSRemapNewInstance(int argc, char* argv[], void** ih, char*
> errbuf, int errbuf_size) {
> + char config_file[PATH_MAX];
> + struct config *cfg;
> +
> + cfg = TSmalloc(sizeof(struct config));
> + *ih = (void *) cfg;
> +
> + int i = 0;
> + for (i = 0; i < MAX_KEY_NUM; i++) {
> + cfg->keys[i][0] = '\0';
> + }
> +
> + if (argc != 3) {
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapNewKeyInstance] -
> Argument count wrong (%d)... Need exactly two pparam= (config file name).",
> + argc);
> + return TS_ERROR;
> + }
> + TSDebug(PLUGIN_NAME, "Initializing remap function of %s -> %s with
> config from %s", argv[0], argv[1], argv[2]);
> + cfg->map_from = TSstrndup( argv[0], strlen(argv[0]));
> + cfg->map_to = TSstrndup( argv[0], strlen(argv[1]));
> +
> + const char* install_dir = TSInstallDirGet();
> + snprintf(config_file, sizeof(config_file), "%s/%s/%s", install_dir,
> "etc/trafficserver", argv[2]);
This should be TSConfigDirGet().
> + TSDebug(PLUGIN_NAME, "config file name: %s", config_file);
> + FILE *file = fopen(config_file, "r");
> + if (file == NULL ) {
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance] - Error
> opening file %s.", config_file);
> + return TS_ERROR;
> + }
> +
> + char line[260];
> + int line_no = 0;
> + int keynum;
> + while (fgets(line, sizeof(line), file) != NULL ) {
> + TSDebug(PLUGIN_NAME, "LINE: %s (%d)", line, (int) strlen(line));
> + line_no++;
> + if (line[0] == '#' || strlen(line) <= 1)
> + continue;
> + char *pos = strchr(line, '=');
> + if (pos == NULL ) {
> + TSError("Error parsing line %d of file %s (%s).",
> line_no, config_file, line);
> + }
> + *pos = '\0';
> + char *value = pos + 1;
> + while (isspace(*value)) // remove whitespace
> + value++;
> + pos = strchr(value, '\n'); // remove the new line,
> terminate the string
> + if (pos != NULL ) {
> + *pos = '\0';
> + } else {
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance]
> - Maximum line (%d) exceeded on line %d.", MAX_KEY_LEN, line_no);
> + return TS_ERROR;
> + }
> + if (strncmp(line, "key", 3) == 0) {
> + if (value != NULL ) {
> + if (strncmp((char *) (line + 3), "0", 1) == 0) {
> + keynum = 0;
> + } else {
> + TSDebug(PLUGIN_NAME, ">>> %s <<<", line +3);
> + keynum = atoi((char *) (line + 3));
> + if (keynum == 0) {
> + keynum = -1; // Not a Number
> + }
> + }
> + TSDebug(PLUGIN_NAME, "key number %d == %s",
> keynum, value);
> + if (keynum > MAX_KEY_NUM || keynum == -1) {
> + snprintf(errbuf, errbuf_size - 1,
> "[TSRemapNewInstance] - Key number (%d) > MAX_KEY_NUM (%d) or NaN.", keynum,
> MAX_KEY_NUM);
> + return TS_ERROR;
> + }
> + strcpy(&cfg->keys[keynum][0], value);
> + }
> + } else if (strncmp(line, "error_url", 9) == 0) {
> + if (atoi(value)) {
> + cfg->err_status = atoi(value);
> + }
> + value += 3;
> + while (isspace(*value))
> + value++;
> +// if (strncmp(value, "http://", strlen("http://")) != 0) {
> +// snprintf(errbuf, errbuf_size - 1,
> +// "[TSRemapNewInstance] - Invalid
> config, err_status == 302, but err_url does not start with \"http://\"");
> +// return TS_ERROR;
> +// }
> + if (cfg->err_status == TS_HTTP_STATUS_MOVED_TEMPORARILY)
> + cfg->err_url = TSstrndup(value, strlen(value));
> + else cfg->err_url = NULL;
> + } else {
> + TSError("Error parsing line %d of file %s (%s).",
> line_no, config_file, line);
> + }
> + }
> +
> + switch (cfg->err_status) {
> + case TS_HTTP_STATUS_MOVED_TEMPORARILY:
> + if (cfg->err_url == NULL ) {
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance]
> - Invalid config, err_status == 302, but err_url == NULL");
> + return TS_ERROR;
> + }
> + printf("[url_sig] mapping {%s -> %s} with status %d and err url
> %s\n", argv[0], argv[1], cfg->err_status, cfg->err_url);
> + break;
> + case TS_HTTP_STATUS_FORBIDDEN:
> + if (cfg->err_url != NULL ) {
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance]
> - Invalid config, err_status == 403, but err_url != NULL");
> + return TS_ERROR;
> + }
> + printf("[url_sig] mapping {%s -> %s} with status %d\n",
> argv[0], argv[1], cfg->err_status);
> + break;
> + default:
> + snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance] -
> Return code %d not supported.", cfg->err_status);
> + return TS_ERROR;
> +
> + }
> +
> + i = 0;
> + for (i = 0; i < MAX_KEY_NUM; i++) {
> + if (cfg->keys[i] != NULL && strlen(cfg->keys[i]) > 0)
> + printf("[url_sig] shared secret key[%d] = %s\n", i,
> cfg->keys[i]);
> + }
> + fclose(file);
> + printf("%s version %s initialized.\n", PLUGIN_NAME, VERSION);
> + return TS_SUCCESS;
> +}
> +
> +void TSRemapDeleteInstance(void* ih) {
> + struct config *cfg;
> + cfg = (struct config *) ih;
> +
> + TSError("Cleaning up...");
> + TSfree(cfg->map_from);
> + TSfree(cfg->map_to);
> + TSfree(cfg->err_url);
> + TSfree(cfg);
> +}
> +
> +void err_log(char *url, char *msg) {
> + if (msg && url) {
> + TSDebug(PLUGIN_NAME, "[URL=%s]: %s", url, msg);
> + TSError("[URL=%s]: %s", url, msg); // This goes to error.log
> + } else {
> + TSError("Invalid err_log request");
> + }
> +}
> +
> +TSRemapStatus TSRemapDoRemap(void* ih, TSHttpTxn txnp, TSRemapRequestInfo
> *rri) {
> + struct config *cfg;
> + cfg = (struct config *) ih;
> +
> + int url_len = 0;
> + time_t expiration = 0;
> + int algorithm = -1;
> + int keyindex = -1;
> + int cmp_res;
> + int rval;
> + int i = 0;
> + int j = 0;
> + unsigned int sig_len = 0;
> +
> + /* all strings are locally allocated except url... about 25k per
> instance*/
> + char *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' };
> + unsigned char sig[MAX_SIG_SIZE + 1];
> + char sig_string[2 * MAX_SIG_SIZE + 1];
> +
> + /* these are just pointers into other allocations */
> + char *signature = NULL;
> + char *parts = NULL;
> + char *part = NULL;
> + char *p = NULL, *pp = NULL;
> + char *query = NULL;
> +
> + int retval, sockfd;
> + socklen_t peer_len;
> + struct sockaddr_in peer;
> +
> + url = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &url_len);
> +
> + if (url_len >= MAX_REQ_LEN - 1) {
> + err_log(url, "URL string too long.");
> + goto deny;
> + }
> +
> + TSDebug(PLUGIN_NAME, "%s", url);
> +
> + query = strstr(url, "?");
> + if (query == NULL ) {
> + err_log(url, "Has no query string.");
> + goto deny;
> + }
> +
> + if (strncmp(url, "http://", strlen("http://")) != 0) {
> + err_log(url, "Invalid URL scheme - only http supported.");
> + goto deny;
> + }
> +
> + /* first, parse the query string */
> + query++; /* get rid of the ? */
> + TSDebug(PLUGIN_NAME, "Query string is:%s", query);
> +
> + // Client IP - this one is optional
> + p = strstr(query, CIP_QSTRING"=");
> + if (p != NULL ) {
> +
> + p += strlen(CIP_QSTRING + 1);
> + pp = strstr(p, "&");
> + if ((pp - p) > CIP_STRLEN - 1 || (pp - p) < 4) {
> + err_log(url, "IP address string too long or short.");
> + goto deny;
> + }
> + strncpy(client_ip, p + strlen(CIP_QSTRING) + 1, (pp - p -
> (strlen(CIP_QSTRING) + 1)));
> + client_ip[pp - p - (strlen(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:");
> + }
What's wrong with TSHttpTxnClientAddrGet()?
> + 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.");
> + goto deny;
> + }
> + }
> + // Expiration
> + p = strstr(query, EXP_QSTRING"=");
> + if (p != NULL ) {
> + p += strlen(EXP_QSTRING) + 1;
> + expiration = atoi(p);
> + if (expiration == 0 || expiration < time(NULL )) {
> + err_log(url, "Invalid expiration, or expired.");
> + goto deny;
> + }
> + TSDebug(PLUGIN_NAME, "Exp: %d", (int) expiration);
> + } else {
> + err_log(url, "Expiration query string not found.");
> + goto deny;
> + }
> + // Algorithm
> + p = strstr(query, ALG_QSTRING"=");
> + if (p != NULL ) {
> + p += strlen(ALG_QSTRING) + 1;
> + algorithm = atoi(p);
> + // The check for a valid algorithm is later.
> + TSDebug(PLUGIN_NAME, "Algorithm: %d", algorithm);
> + } else {
> + err_log(url, "Algorithm query string not found.");
> + goto deny;
> + }
> + // Key index
> + p = strstr(query, KIN_QSTRING"=");
> + if (p != NULL ) {
> + p += strlen(KIN_QSTRING) + 1;
> + keyindex = atoi(p);
> + if (keyindex == -1) {
> + err_log(url, "Invalid key index.");
> + goto deny;
> + }
> + TSDebug(PLUGIN_NAME, "Key Index: %d", keyindex);
> + } else {
> + err_log(url, "KeyIndex query string not found.");
> + goto deny;
> + }
> + // Parts
> + p = strstr(query, PAR_QSTRING"=");
> + if (p != NULL ) {
> + p += strlen(PAR_QSTRING) + 1;
> + parts = p; // NOTE parts is not NULL terminated it is
> terminated by "&" of next param
> + p = strstr(parts, "&");
> + TSDebug(PLUGIN_NAME, "Parts: %.*s", (int) (p - parts), parts);
> + } else {
> + err_log(url, "PartsSigned query string not found.");
> + goto deny;
> + }
> + // And finally, the sig (has to be last)
> + p = strstr(query, SIG_QSTRING"=");
> + if (p != NULL ) {
> + p += strlen(SIG_QSTRING) + 1;
> + signature = p; // NOTE sig is not NULL terminated, it has to be
> 20 chars
> + if ((algorithm == USIG_HMAC_SHA1 && strlen(signature) <
> SHA1_SIG_SIZE) || (algorithm == USIG_HMAC_MD5 && strlen(signature) <
> MD5_SIG_SIZE)) {
> + err_log(url, "Signature query string too short (<
> 20).");
> + goto deny;
> + }
> + } else {
> + err_log(url, "Signature query string not found.");
> + goto deny;
> + }
> +
> + /* have the query string, and parameters passed initial checks */
> + TSDebug(PLUGIN_NAME, "Found all needed parameters: C=%s E=%d A=%d K=%d
> P=%s S=%s", client_ip, (int) expiration, algorithm, 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 */
> + p = strstr(url, "?");
> + memcpy(urltokstr, &url[strlen("http://")], p - url - strlen("http://"));
> + part = strtok_r(urltokstr, "/", &p);
> + while (part != NULL ) {
> + if (parts[j] == '1') {
> + strcpy(signed_part + strlen(signed_part), part);
> + strcpy(signed_part + strlen(signed_part), "/");
> + }
> + if (parts[j + 1] == '0' || parts[j + 1] == '1') // This
> remembers the last part, meaning, if there are no more valid letters in parts
> + j++; // will
> keep repeating the value of the last one
> + part = strtok_r(NULL, "/", &p);
> + }
> +
> + signed_part[strlen(signed_part) - 1] = '?'; // chop off the last /,
> replace with '?'
> + p = strstr(query, SIG_QSTRING"=");
> + strncat(signed_part, query, (p - query) + strlen(SIG_QSTRING) + 1);
> +
> + TSDebug(PLUGIN_NAME, "Signed string=\"%s\"", signed_part);
> +
> + /* calculate the expected the signature with the right algorithm */
> + switch (algorithm) {
> + case USIG_HMAC_SHA1:
> + HMAC(EVP_sha1(), (const unsigned char *) cfg->keys[keyindex],
> strlen(cfg->keys[keyindex]), (const unsigned char *) signed_part,
> + strlen(signed_part), sig, &sig_len);
> + if (sig_len != SHA1_SIG_SIZE) {
> + TSDebug(PLUGIN_NAME, "sig_len: %d", sig_len);
> + err_log(url, "Calculated sig len != SHA1_SIG_SIZE !");
> + goto deny;
> + }
> +
> + break;
> + case USIG_HMAC_MD5:
> + HMAC(EVP_md5(), (const unsigned char *) cfg->keys[keyindex],
> strlen(cfg->keys[keyindex]), (const unsigned char *) signed_part,
> + strlen(signed_part), sig, &sig_len);
> + if (sig_len != MD5_SIG_SIZE) {
> + TSDebug(PLUGIN_NAME, "sig_len: %d", sig_len);
> + err_log(url, "Calculated sig len != MD5_SIG_SIZE !");
> + goto deny;
> + }
> + break;
> + default:
> + err_log(url, "Algorithm not supported.");
> + goto deny;
> + }
> +
> + for (i = 0; i < sig_len; i++) {
> + sprintf(&(sig_string[i * 2]), "%02x", sig[i]);
> + }
> +
> + TSDebug(PLUGIN_NAME, "Expected signature: %s", sig_string);
> +
> + /* and compare to signature that was sent */
> + cmp_res = strncmp(sig_string, signature, sig_len * 2);
> + if (cmp_res != 0) {
> + err_log(url, "Signature check failed.");
> + goto deny;
> + } else {
> + TSDebug(PLUGIN_NAME, "Signature check passed.");
> + goto allow;
> + }
> +
> + /* ********* Deny ********* */
> + deny: if (url)
> + TSfree(url);
> + switch (cfg->err_status) {
> + case TS_HTTP_STATUS_MOVED_TEMPORARILY:
> + TSDebug(PLUGIN_NAME, "Redirecting to %s", cfg->err_url);
> + char *start, *end;
> + start = cfg->err_url;
> + end = start + strlen(cfg->err_url);
> + if (TSUrlParse(rri->requestBufp, rri->requestUrl, (const char
> **) &start, end) != TS_PARSE_DONE) {
> + err_log("url", "Error inn TSUrlParse!");
> + }
> + rri->redirect = 1;
> + break;
> + case TS_HTTP_STATUS_FORBIDDEN:
> + default:
> + /* set status and body to be 403 */
> + TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_FORBIDDEN);
> + TSHttpTxnErrorBodySet(txnp, TSstrdup("Authorization Denied"),
> strlen("Authorization Denied") - 1, TSstrdup("text/plain"));
> + break;
> + }
> + return TSREMAP_DID_REMAP;
> +
> + /* ********* Allow ********* */
> + allow: if (url)
> + TSfree(url);
> + /* drop the query string so we can cache-hit */
> + rval = TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, NULL, 0);
You can't sign URLs with query parameters?
> + if (rval != TS_SUCCESS) {
> + TSError("Error stripping query string: %d.", rval);
> + }
> + return TSREMAP_NO_REMAP;
> +}
>
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/88f18501/plugins/experimental/url_sig/url_sig.h
> ----------------------------------------------------------------------
> diff --git a/plugins/experimental/url_sig/url_sig.h
> b/plugins/experimental/url_sig/url_sig.h
> new file mode 100644
> index 0000000..c495b4b
> --- /dev/null
> +++ b/plugins/experimental/url_sig/url_sig.h
> @@ -0,0 +1,52 @@
> +/** @file
> + Licensed to the Apache Software Foundation (ASF) under one
> + or more contributor license agreements. See the NOTICE file
> + distributed with this work for additional information
> + regarding copyright ownership. The ASF licenses this file
> + to you under the Apache License, Version 2.0 (the
> + "License"); you may not use this file except in compliance
> + with the License. You may obtain a copy of the License at
> +
> + http://www.apache.org/licenses/LICENSE-2.0
> +
> + Unless required by applicable law or agreed to in writing, software
> + distributed under the License is distributed on an "AS IS" BASIS,
> + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + See the License for the specific language governing permissions and
> + limitations under the License.
> + */
> +
> +#ifndef URL_SIG_H_
> +#define URL_SIG_H_
> +
> +#define VERSION "1.0"
> +/* in the query string that we add to sign the url: */
> +#define CIP_QSTRING "C" /* C=24.0.33.12 designates the client IP address */
> +#define EXP_QSTRING "E" /* E=1356128799 means expires at (seconds since
> Unix epoch) */
> +#define ALG_QSTRING "A" /* A=1 means hashing algorithm 1 */
> +#define KIN_QSTRING "K" /* K=3 means use key number 3 */
> +#define PAR_QSTRING "P" /* P=1110 means use parts 0, 1 and 2 (and no more)
> for the hashing of the url after removing the 'http://' */
> + /* and making the parts by doing a split("/") */
> +#define SIG_QSTRING "S" /* S=9e2828d570a4bee3c964f698b0985ee58b9f6b64 means
> 9e2828d570a4bee3c964f698b0985ee58b9f6b64 is the sig
> + This one has to be the last one of the string */
> +
> +#define CIP_STRLEN 20
> +#define EXP_STRLEN 16
> +#define PAR_STRLEN 16
> +#define MAX_PARTS 32
> +
> +#define MAX_HTTP_REQUEST_SIZE 8192 //
> +
> +#define MAX_SIG_SIZE 20
> +#define SHA1_SIG_SIZE 20
> +#define MD5_SIG_SIZE 16
> +
> +#define MAX_REQ_LEN 8192
> +#define MAX_KEY_LEN 256
> +#define MAX_KEY_NUM 16
> +
> +#define USIG_HMAC_SHA1 1
> +#define USIG_HMAC_MD5 2
> +
> +
> +#endif /* URL_SIG_H_ */
>