Skip to content

Commit 666001c

Browse files
yatsukhnenkojpeach
authored andcommitted
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.
1 parent e70d480 commit 666001c

File tree

3 files changed

+213
-0
lines changed

3 files changed

+213
-0
lines changed

example/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ plugins = \
3737
remap.la \
3838
replace-header.la \
3939
response-header-1.la \
40+
secure-link.la \
4041
server-transform.la \
4142
thread-1.la \
4243
version.la
@@ -68,6 +69,7 @@ server_transform_la_SOURCES = server-transform/server-transform.c
6869
thread_1_la_SOURCES = thread-1/thread-1.c
6970
psi_la_SOURCES = thread-pool/psi.c thread-pool/thread.c
7071
version_la_SOURCES = version/version.c
72+
secure_link_la_SOURCES = secure-link/secure-link.c
7173

7274
# The following examples do not build:
7375
#

example/secure-link/readme.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
The secure-link plugin allows Traffic Server to protect resources by verifying
2+
checksum value passed in the request and computed for the request.
3+
Checksum is the md5 digest of concatenation of several params:
4+
[secret] + [Client IP Address] + [HTTP Query Path] + [expire]
5+
The plugin can be used in the plugins chain in remap.config and it expects two @pparams:
6+
1. secret - the word, which is known only to the application that generated link
7+
and Traffic Server.
8+
2. policy - if set to 'strict' and checksums not match or expire value
9+
lower than current time the client will receive 403 Frobidden response.
10+
Used for debugging.
11+
12+
For example request
13+
http://foo.example.com/d41d8cd98f00b204e9800998ecf8427e/52b9ed11/path/to/secret/document.pdf
14+
may be remapped to
15+
http://bar.example.com/path/to/secret/document.pdf?st=d41d8cd98f00b204e9800998ecf8427e&ex=52b9ed11
16+
and then passed to secure-link plugin, which compare 'st' and 'ex' GET params
17+
with computed md5 checksum and current time respectively.
18+
If check passed the plugin removes 'st' and 'ex' GET params and passes down
19+
the processing chain;

example/secure-link/secure-link.c

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/** @file
2+
3+
A brief file description
4+
5+
@section license License
6+
7+
Licensed to the Apache Software Foundation (ASF) under one
8+
or more contributor license agreements. See the NOTICE file
9+
distributed with this work for additional information
10+
regarding copyright ownership. The ASF licenses this file
11+
to you under the Apache License, Version 2.0 (the
12+
"License"); you may not use this file except in compliance
13+
with the License. You may obtain a copy of the License at
14+
15+
http://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
See the License for the specific language governing permissions and
21+
limitations under the License.
22+
*/
23+
24+
#include <time.h>
25+
#include <stdio.h>
26+
#include <stdlib.h>
27+
#include <string.h>
28+
#include <sys/socket.h>
29+
#include <netinet/in.h>
30+
#include <arpa/inet.h>
31+
#include <openssl/md5.h>
32+
33+
#include "ts/ts.h"
34+
#include "ts/remap.h"
35+
36+
#define PLUGIN_NAME "secure_link"
37+
38+
typedef struct {
39+
char *secret;
40+
uint8_t strict;
41+
} secure_link_info;
42+
43+
TSRemapStatus
44+
TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo* rri)
45+
{
46+
int i, len;
47+
time_t t, e;
48+
MD5_CTX ctx;
49+
struct sockaddr_in *in;
50+
const char *qh, *ph, *ip;
51+
char *s, *ptr, *val, hash[32];
52+
unsigned char md[MD5_DIGEST_LENGTH];
53+
secure_link_info *sli = (secure_link_info *)ih;
54+
char *token = NULL, *expire = NULL, *path = NULL;
55+
56+
in = (struct sockaddr_in *)TSHttpTxnClientAddrGet(rh);
57+
ip = inet_ntoa(in->sin_addr);
58+
s = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &len);
59+
TSDebug(PLUGIN_NAME, "request [%.*s] from [%s]", len, s, ip);
60+
TSfree(s);
61+
62+
qh = TSUrlHttpQueryGet(rri->requestBufp, rri->requestUrl, &len);
63+
if(qh && len > 0) {
64+
s = (char *)TSstrndup(qh, len);
65+
if((ptr = strtok(s, "&")) != NULL) {
66+
do {
67+
if((val = strchr(ptr, '=')) != NULL) {
68+
*val++ = '\0';
69+
if(strcmp(ptr, "st") == 0) {
70+
token = TSstrdup(val);
71+
} else if(strcmp(ptr, "ex") == 0) {
72+
expire = TSstrdup(val);
73+
}
74+
} else {
75+
TSError("Invalid parameter [%s]", ptr);
76+
break;
77+
}
78+
} while((ptr = strtok(NULL, "&")) != NULL);
79+
}
80+
TSfree(s);
81+
} else {
82+
TSError("TSUrlHttpQueryGet returns empty value");
83+
/* this is just example, so set fake params to prevent plugin crash */
84+
token = TSstrdup("d41d8cd98f00b204e9800998ecf8427e");
85+
expire = TSstrdup("00000000");
86+
}
87+
88+
ph = TSUrlPathGet(rri->requestBufp, rri->requestUrl, &len);
89+
if(ph && len > 0) {
90+
s = TSstrndup(ph, len);
91+
if((ptr = strrchr(s, '/')) != NULL) {
92+
*++ptr = '\0';
93+
}
94+
path = TSstrdup(s);
95+
TSfree(s);
96+
} else {
97+
TSError("TSUrlPathGet returns empty value");
98+
/* this is just example, so set fake params to prevent plugin crash */
99+
path = TSstrdup("example/");
100+
}
101+
MD5_Init(&ctx);
102+
MD5_Update(&ctx, sli->secret, strlen(sli->secret));
103+
MD5_Update(&ctx, ip, strlen(ip));
104+
MD5_Update(&ctx, path, strlen(path));
105+
MD5_Update(&ctx, expire, strlen(expire));
106+
MD5_Final(md, &ctx);
107+
for(i = 0; i < MD5_DIGEST_LENGTH; i++) {
108+
sprintf(&hash[i * 2], "%02x", md[i]);
109+
}
110+
time(&t);
111+
e = strtol(expire, NULL, 16);
112+
i = TSREMAP_DID_REMAP;
113+
if(e < t || strcmp(hash, token) != 0) {
114+
if(e < t) {
115+
TSDebug(PLUGIN_NAME, "link expired: [%lu] vs [%lu]", t, e);
116+
} else {
117+
TSDebug(PLUGIN_NAME, "tokens mismatch: [%s] vs [%s]", hash, token);
118+
}
119+
if(sli->strict) {
120+
TSDebug(PLUGIN_NAME, "request is DENY");
121+
TSHttpTxnSetHttpRetStatus(rh, TS_HTTP_STATUS_FORBIDDEN);
122+
i = TSREMAP_NO_REMAP;
123+
} else {
124+
TSDebug(PLUGIN_NAME, "request is PASS");
125+
}
126+
}
127+
if(i == TSREMAP_DID_REMAP) {
128+
if(TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, "", -1) == TS_SUCCESS) {
129+
s = TSUrlStringGet(rri->requestBufp, rri->requestUrl, &len);
130+
TSDebug(PLUGIN_NAME, "new request string is [%.*s]", len, s);
131+
TSfree(s);
132+
} else {
133+
i = TSREMAP_NO_REMAP;
134+
}
135+
}
136+
TSfree(expire);
137+
TSfree(token);
138+
TSfree(path);
139+
return i;
140+
}
141+
142+
TSReturnCode
143+
TSRemapNewInstance(int argc, char **argv, void **ih, char *errbuf, int errbuf_size)
144+
{
145+
int i;
146+
char *ptr;
147+
secure_link_info *sli;
148+
149+
sli = (secure_link_info *)TSmalloc(sizeof(secure_link_info));
150+
sli->secret = NULL;
151+
sli->strict = 0;
152+
153+
for(i = 2; i < argc; i++) {
154+
if((ptr = strchr(argv[i], ':')) != NULL) {
155+
*ptr++ = '\0';
156+
if(strcmp(argv[i], "secret") == 0) {
157+
if(sli->secret != NULL) {
158+
TSfree(sli->secret);
159+
}
160+
sli->secret = TSstrdup(ptr);
161+
} else if(strcmp(argv[i], "policy") == 0) {
162+
sli->strict = !strcasecmp(ptr, "strict");
163+
} else {
164+
TSDebug(PLUGIN_NAME, "Unknown parameter [%s]", argv[i]);
165+
}
166+
} else {
167+
TSError("Invalid parameter [%s]", argv[i]);
168+
}
169+
}
170+
171+
if(sli->secret == NULL) {
172+
sli->secret = TSstrdup("");
173+
}
174+
175+
*ih = (void *)sli;
176+
return TS_SUCCESS;
177+
}
178+
179+
void
180+
TSRemapDeleteInstance(void *ih)
181+
{
182+
secure_link_info *sli = (secure_link_info *)ih;
183+
184+
TSfree(sli->secret);
185+
TSfree(sli);
186+
}
187+
188+
TSReturnCode
189+
TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
190+
{
191+
return TS_SUCCESS;
192+
}

0 commit comments

Comments
 (0)