Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions example/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ noinst_LTLIBRARIES = \
remap.la \
replace-header.la \
response-header-1.la \
secure-link.la \
server-transform.la \
thread-1.la \
version.la
Expand All @@ -62,6 +63,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:
#
Expand Down
19 changes: 19 additions & 0 deletions example/secure-link/readme.txt
Original file line number Diff line number Diff line change
@@ -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;
169 changes: 169 additions & 0 deletions example/secure-link/secure-link.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#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;
}