1'''
2'''
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#      http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing, software
14#  distributed under the License is distributed on an "AS IS" BASIS,
15#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16#  See the License for the specific language governing permissions and
17#  limitations under the License.
18
19import hashlib
20import hmac
21
22Test.Summary = '''
23Test url_sig plugin
24'''
25
26Test.ContinueOnFail = True
27
28# Skip if plugins not present.
29Test.SkipUnless(Condition.PluginExists('url_sig.so'))
30
31# Set up to check the output after the tests have run.
32#
33url_sig_log_id = Test.Disk.File("url_sig_short.log")
34url_sig_log_id.Content = "url_sig.gold"
35
36server = Test.MakeOriginServer("server")
37
38request_header = {
39    "headers": "GET /foo/abcde/qrstuvwxyz HTTP/1.1\r\nHost: just.any.thing\r\n\r\n", "timestamp": "1469733493.993", "body": ""
40}
41# expected response from the origin server
42response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
43# add response to the server dictionary
44server.addResponse("sessionfile.log", request_header, response_header)
45
46# Define default ATS. Disable the cache to make sure each request is forwarded
47# to the origin server.
48ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True, enable_cache=False)
49
50ts.addDefaultSSLFiles()
51
52ts.Disk.records_config.update({
53    # 'proxy.config.diags.debug.enabled': 1,
54    # 'proxy.config.diags.debug.tags': 'http|url_sig',
55    'proxy.config.proxy_name': 'Poxy_Proxy',  # This will be the server name.
56    'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
57    'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
58})
59
60ts.Disk.ssl_multicert_config.AddLine(
61    'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
62)
63
64# Use unchanged incoming URL.
65#
66ts.Disk.remap_config.AddLine(
67    'map http://one.two.three/ http://127.0.0.1:{}/'.format(server.Variables.Port) +
68    ' @plugin=url_sig.so @pparam={}/url_sig.config'.format(Test.TestDirectory)
69)
70
71# Use unchanged incoming HTTPS URL.
72#
73ts.Disk.remap_config.AddLine(
74    'map https://one.two.three/ http://127.0.0.1:{}/'.format(server.Variables.Port) +
75    ' @plugin=url_sig.so @pparam={}/url_sig.config'.format(Test.TestDirectory)
76)
77
78# Use pristine URL, incoming URL unchanged.
79#
80ts.Disk.remap_config.AddLine(
81    'map http://four.five.six/ http://127.0.0.1:{}/'.format(server.Variables.Port) +
82    ' @plugin=url_sig.so @pparam={}/url_sig.config @pparam=pristineurl'.format(Test.TestDirectory)
83)
84
85# Use pristine URL, incoming URL changed.
86#
87ts.Disk.remap_config.AddLine(
88    'map http://seven.eight.nine/ http://127.0.0.1:{}'.format(server.Variables.Port) +
89    ' @plugin=url_sig.so @pparam={}/url_sig.config @pparam=PristineUrl'.format(Test.TestDirectory)
90)
91
92# Validation failure tests.
93
94LogTee = " 2>&1 | grep '^<' | tee -a {}/url_sig_long.log".format(Test.RunDirectory)
95
96# Bad client / MD5 / P=101 / URL pristine / URL altered.
97#
98tr = Test.AddTestRun()
99tr.Processes.Default.StartBefore(ts)
100tr.Processes.Default.StartBefore(server, ready=When.PortOpen(server.Variables.Port))
101tr.Processes.Default.ReturnCode = 0
102tr.Processes.Default.Command = (
103    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
104    "foo/abcde/qrstuvwxyz?C=127.0.0.2&E=33046620008&A=2&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" +
105    LogTee
106)
107
108# With client / MD5 / P=010 / URL pristine / URL altered -- Expired.
109#
110tr = Test.AddTestRun()
111tr.Processes.Default.ReturnCode = 0
112tr.Processes.Default.Command = (
113    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
114    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=1&A=2&K=13&P=010&S=f237aad1fa010234d7bf8108a0e36387'" +
115    LogTee
116)
117
118# With client / No algorithm / P=101 / URL pristine / URL altered.
119#
120tr = Test.AddTestRun()
121tr.Processes.Default.ReturnCode = 0
122tr.Processes.Default.Command = (
123    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
124    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" +
125    LogTee
126)
127
128# With client / Bad algorithm / P=101 / URL pristine / URL altered.
129#
130tr = Test.AddTestRun()
131tr.Processes.Default.ReturnCode = 0
132tr.Processes.Default.Command = (
133    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
134    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=3&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" +
135    LogTee
136)
137
138# With client / MD5 / No parts / URL pristine / URL altered.
139#
140tr = Test.AddTestRun()
141tr.Processes.Default.ReturnCode = 0
142tr.Processes.Default.Command = (
143    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
144    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&S=d1f352d4f1d931ad2f441013402d93f8'" +
145    LogTee
146)
147
148# With client / MD5 / P=10 (bad) / URL pristine / URL altered.
149#
150tr = Test.AddTestRun()
151tr.Processes.Default.ReturnCode = 0
152tr.Processes.Default.Command = (
153    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
154    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=10&S=d1f352d4f1d931ad2f441013402d93f8'" +
155    LogTee
156)
157
158# With client / MD5 / P=101 / URL pristine / URL altered -- No signature.
159#
160tr = Test.AddTestRun()
161tr.Processes.Default.ReturnCode = 0
162tr.Processes.Default.Command = (
163    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
164    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=101'" +
165    LogTee
166)
167
168# With client / MD5 / P=101 / URL pristine / URL altered  -- Bad signature.
169#
170tr = Test.AddTestRun()
171tr.Processes.Default.ReturnCode = 0
172tr.Processes.Default.Command = (
173    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
174    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=101&S=d1f452d4f1d931ad2f441013402d93f8'" +
175    LogTee
176)
177
178# With client / MD5 / P=101 / URL pristine / URL altered -- Spurious &.
179#
180tr = Test.AddTestRun()
181tr.Processes.Default.ReturnCode = 0
182tr.Processes.Default.Command = (
183    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
184    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8#'" +
185    LogTee
186)
187
188# Success tests.
189
190# With client / SHA1 / P=1 / URL pristine / URL not altered.
191#
192tr = Test.AddTestRun()
193tr.Processes.Default.ReturnCode = 0
194tr.Processes.Default.Command = (
195    "curl --verbose --proxy http://127.0.0.1:{} 'http://four.five.six/".format(ts.Variables.port) +
196    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046618556&A=1&K=15&P=1&S=f4103561a23adab7723a89b9831d77e0afb61d92'" +
197    LogTee
198)
199
200# No client / MD5 / P=1 / URL pristine / URL altered.
201#
202tr = Test.AddTestRun()
203tr.Processes.Default.ReturnCode = 0
204tr.Processes.Default.Command = (
205    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
206    "foo/abcde/qrstuvwxyz?E=33046618586&A=2&K=0&P=1&S=0364efa28afe345544596705b92d20ac'" +
207    LogTee
208)
209
210# With client / MD5 / P=010 / URL pristine / URL altered.
211#
212tr = Test.AddTestRun()
213tr.Processes.Default.ReturnCode = 0
214tr.Processes.Default.Command = (
215    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
216    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046619717&A=2&K=13&P=010&S=f237aad1fa010234d7bf8108a0e36387'" +
217    LogTee
218)
219
220# With client / MD5 / P=101 / URL pristine / URL altered.
221#
222tr = Test.AddTestRun()
223tr.Processes.Default.ReturnCode = 0
224tr.Processes.Default.Command = (
225    "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) +
226    "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" +
227    LogTee
228)
229
230
231def sign(payload, key):
232    secret = bytes(key, 'utf-8')
233    data = bytes(payload, 'utf-8')
234    md = bytes(hmac.new(secret, data, digestmod=hashlib.sha1).digest().hex(), 'utf-8')
235    return md.decode("utf-8")
236
237
238# No client / SHA1 / P=1 / URL not pristine / URL not altered.
239#
240path = "foo/abcde/qrstuvwxyz?E=33046618506&A=1&K=7&P=1&S="
241to_sign = "127.0.0.1:{}/".format(server.Variables.Port) + path
242url = "http://one.two.three/" + path + sign(to_sign, "dqsgopTSM_doT6iAysasQVUKaPykyb6e")
243
244tr = Test.AddTestRun()
245tr.Processes.Default.ReturnCode = 0
246tr.Processes.Default.Command = (
247    "curl --verbose --proxy http://127.0.0.1:{} '{}'".format(ts.Variables.port, url) + LogTee
248)
249
250# No client / SHA1 / P=1 / URL not pristine / URL not altered -- HTTPS.
251#
252path = "foo/abcde/qrstuvwxyz?E=33046618506&A=1&K=7&P=1&S="
253to_sign = "127.0.0.1:{}/".format(server.Variables.Port) + path
254url = "https://127.0.0.1:{}/".format(ts.Variables.ssl_port) + path + sign(to_sign, "dqsgopTSM_doT6iAysasQVUKaPykyb6e")
255
256tr = Test.AddTestRun()
257tr.Processes.Default.ReturnCode = 0
258tr.Processes.Default.Command = (
259    "curl --verbose --http1.1 --insecure --header 'Host: one.two.three' '{}'".format(url) +
260    LogTee + " ; grep -F -e '< HTTP' -e Authorization {0}/url_sig_long.log > {0}/url_sig_short.log ".format(ts.RunDirectory)
261)
262
263# Overriding the built in ERROR check since we expect some ERROR messages
264ts.Disk.diags_log.Content = Testers.ContainsExpression("ERROR", "Some tests are failure tests")
265ts.Disk.diags_log.Content += Testers.ExcludesExpression("Error parsing", "Verify that we can accept long comment lines")
266