Repository: trafficserver Updated Branches: refs/heads/master e70d480da -> 666001cfa
Add new secure link example plugin Add an example plugin demonstrating one method of authorizing URL access based on a shared secret. This closes #27. Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/666001cf Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/666001cf Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/666001cf Branch: refs/heads/master Commit: 666001cfa2eed84c9b571bac5194060637aadcfa Parents: e70d480 Author: Pavel Yatsukhnenko <[email protected]> Authored: Tue Feb 25 16:19:26 2014 -0800 Committer: James Peach <[email protected]> Committed: Tue Feb 25 16:21:36 2014 -0800 ---------------------------------------------------------------------- example/Makefile.am | 2 + example/secure-link/readme.txt | 19 ++++ example/secure-link/secure-link.c | 192 +++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/trafficserver/blob/666001cf/example/Makefile.am ---------------------------------------------------------------------- diff --git a/example/Makefile.am b/example/Makefile.am index b79bbee..c459991 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -37,6 +37,7 @@ plugins = \ remap.la \ replace-header.la \ response-header-1.la \ + secure-link.la \ server-transform.la \ thread-1.la \ version.la @@ -68,6 +69,7 @@ server_transform_la_SOURCES = server-transform/server-transform.c thread_1_la_SOURCES = thread-1/thread-1.c psi_la_SOURCES = thread-pool/psi.c thread-pool/thread.c version_la_SOURCES = version/version.c +secure_link_la_SOURCES = secure-link/secure-link.c # The following examples do not build: # http://git-wip-us.apache.org/repos/asf/trafficserver/blob/666001cf/example/secure-link/readme.txt ---------------------------------------------------------------------- diff --git a/example/secure-link/readme.txt b/example/secure-link/readme.txt new file mode 100644 index 0000000..e79dab0 --- /dev/null +++ b/example/secure-link/readme.txt @@ -0,0 +1,19 @@ +The secure-link plugin allows Traffic Server to protect resources by verifying +checksum value passed in the request and computed for the request. +Checksum is the md5 digest of concatenation of several params: + [secret] + [Client IP Address] + [HTTP Query Path] + [expire] +The plugin can be used in the plugins chain in remap.config and it expects two @pparams: +1. secret - the word, which is known only to the application that generated link + and Traffic Server. +2. policy - if set to 'strict' and checksums not match or expire value + lower than current time the client will receive 403 Frobidden response. + Used for debugging. + +For example request + http://foo.example.com/d41d8cd98f00b204e9800998ecf8427e/52b9ed11/path/to/secret/document.pdf +may be remapped to + http://bar.example.com/path/to/secret/document.pdf?st=d41d8cd98f00b204e9800998ecf8427e&ex=52b9ed11 +and then passed to secure-link plugin, which compare 'st' and 'ex' GET params +with computed md5 checksum and current time respectively. +If check passed the plugin removes 'st' and 'ex' GET params and passes down +the processing chain; http://git-wip-us.apache.org/repos/asf/trafficserver/blob/666001cf/example/secure-link/secure-link.c ---------------------------------------------------------------------- diff --git a/example/secure-link/secure-link.c b/example/secure-link/secure-link.c new file mode 100644 index 0000000..67a75b3 --- /dev/null +++ b/example/secure-link/secure-link.c @@ -0,0 +1,192 @@ +/** @file + + A brief file description + + @section license License + + 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 <time.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <openssl/md5.h> + +#include "ts/ts.h" +#include "ts/remap.h" + +#define PLUGIN_NAME "secure_link" + +typedef struct { + char *secret; + uint8_t strict; +} secure_link_info; + +TSRemapStatus +TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo* rri) +{ + int i, len; + time_t t, e; + MD5_CTX ctx; + struct sockaddr_in *in; + const char *qh, *ph, *ip; + char *s, *ptr, *val, hash[32]; + unsigned char md[MD5_DIGEST_LENGTH]; + secure_link_info *sli = (secure_link_info *)ih; + char *token = NULL, *expire = NULL, *path = NULL; + + in = (struct sockaddr_in *)TSHttpTxnClientAddrGet(rh); + ip = inet_ntoa(in->sin_addr); + s = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &len); + TSDebug(PLUGIN_NAME, "request [%.*s] from [%s]", len, s, ip); + TSfree(s); + + qh = TSUrlHttpQueryGet(rri->requestBufp, rri->requestUrl, &len); + if(qh && len > 0) { + s = (char *)TSstrndup(qh, len); + if((ptr = strtok(s, "&")) != NULL) { + do { + if((val = strchr(ptr, '=')) != NULL) { + *val++ = '\0'; + if(strcmp(ptr, "st") == 0) { + token = TSstrdup(val); + } else if(strcmp(ptr, "ex") == 0) { + expire = TSstrdup(val); + } + } else { + TSError("Invalid parameter [%s]", ptr); + break; + } + } while((ptr = strtok(NULL, "&")) != NULL); + } + TSfree(s); + } else { + TSError("TSUrlHttpQueryGet returns empty value"); + /* this is just example, so set fake params to prevent plugin crash */ + token = TSstrdup("d41d8cd98f00b204e9800998ecf8427e"); + expire = TSstrdup("00000000"); + } + + ph = TSUrlPathGet(rri->requestBufp, rri->requestUrl, &len); + if(ph && len > 0) { + s = TSstrndup(ph, len); + if((ptr = strrchr(s, '/')) != NULL) { + *++ptr = '\0'; + } + path = TSstrdup(s); + TSfree(s); + } else { + TSError("TSUrlPathGet returns empty value"); + /* this is just example, so set fake params to prevent plugin crash */ + path = TSstrdup("example/"); + } + MD5_Init(&ctx); + MD5_Update(&ctx, sli->secret, strlen(sli->secret)); + MD5_Update(&ctx, ip, strlen(ip)); + MD5_Update(&ctx, path, strlen(path)); + MD5_Update(&ctx, expire, strlen(expire)); + MD5_Final(md, &ctx); + for(i = 0; i < MD5_DIGEST_LENGTH; i++) { + sprintf(&hash[i * 2], "%02x", md[i]); + } + time(&t); + e = strtol(expire, NULL, 16); + i = TSREMAP_DID_REMAP; + if(e < t || strcmp(hash, token) != 0) { + if(e < t) { + TSDebug(PLUGIN_NAME, "link expired: [%lu] vs [%lu]", t, e); + } else { + TSDebug(PLUGIN_NAME, "tokens mismatch: [%s] vs [%s]", hash, token); + } + if(sli->strict) { + TSDebug(PLUGIN_NAME, "request is DENY"); + TSHttpTxnSetHttpRetStatus(rh, TS_HTTP_STATUS_FORBIDDEN); + i = TSREMAP_NO_REMAP; + } else { + TSDebug(PLUGIN_NAME, "request is PASS"); + } + } + if(i == TSREMAP_DID_REMAP) { + if(TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, "", -1) == TS_SUCCESS) { + s = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &len); + TSDebug(PLUGIN_NAME, "new request string is [%.*s]", len, s); + TSfree(s); + } else { + i = TSREMAP_NO_REMAP; + } + } + TSfree(expire); + TSfree(token); + TSfree(path); + return i; +} + +TSReturnCode +TSRemapNewInstance(int argc, char **argv, void **ih, char *errbuf, int errbuf_size) +{ + int i; + char *ptr; + secure_link_info *sli; + + sli = (secure_link_info *)TSmalloc(sizeof(secure_link_info)); + sli->secret = NULL; + sli->strict = 0; + + for(i = 2; i < argc; i++) { + if((ptr = strchr(argv[i], ':')) != NULL) { + *ptr++ = '\0'; + if(strcmp(argv[i], "secret") == 0) { + if(sli->secret != NULL) { + TSfree(sli->secret); + } + sli->secret = TSstrdup(ptr); + } else if(strcmp(argv[i], "policy") == 0) { + sli->strict = !strcasecmp(ptr, "strict"); + } else { + TSDebug(PLUGIN_NAME, "Unknown parameter [%s]", argv[i]); + } + } else { + TSError("Invalid parameter [%s]", argv[i]); + } + } + + if(sli->secret == NULL) { + sli->secret = TSstrdup(""); + } + + *ih = (void *)sli; + return TS_SUCCESS; +} + +void +TSRemapDeleteInstance(void *ih) +{ + secure_link_info *sli = (secure_link_info *)ih; + + TSfree(sli->secret); + TSfree(sli); +} + +TSReturnCode +TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size) +{ + return TS_SUCCESS; +}
