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
@@ -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
+               $
+
+       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]);
+       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:");
+               }
+               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);
+       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_ */

Reply via email to