This is an automated email from the ASF dual-hosted git repository. amc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push: new 5fe7236 Add pparam to url_sig plugin to make it authenticate pristine URL, eliminate sheme check, other minor changes. 5fe7236 is described below commit 5fe7236975506514ed79d9f624c28bb84e60af60 Author: Walt Karas <wka...@yahoo-inc.com> AuthorDate: Tue Nov 7 17:11:10 2017 +0000 Add pparam to url_sig plugin to make it authenticate pristine URL, eliminate sheme check, other minor changes. --- doc/admin-guide/plugins/url_sig.en.rst | 10 +- plugins/experimental/url_sig/sign.pl | 8 +- plugins/experimental/url_sig/url_sig.c | 236 +++++++++++-------- tests/gold_tests/logging/ccid_ctid.test.py | 2 +- tests/gold_tests/pluginTest/url_sig/run_sign.sh | 138 +++++++++++ tests/gold_tests/pluginTest/url_sig/url_sig.config | 17 ++ tests/gold_tests/pluginTest/url_sig/url_sig.gold | 24 ++ .../gold_tests/pluginTest/url_sig/url_sig.test.py | 261 +++++++++++++++++++++ 8 files changed, 592 insertions(+), 104 deletions(-) diff --git a/doc/admin-guide/plugins/url_sig.en.rst b/doc/admin-guide/plugins/url_sig.en.rst index cabbcf1..caee1cf 100644 --- a/doc/admin-guide/plugins/url_sig.en.rst +++ b/doc/admin-guide/plugins/url_sig.en.rst @@ -110,12 +110,14 @@ To require a valid signature, verified by a key from the list you generated earlier, modify your :file:`remap.config` configuration to include this plugin for any rules you wish it to affect. -Two parameters for each remap rule are required:: +Two parameters for each remap rule are required, and a third one is optional:: - @plugin=url_sig.so @pparam=<config file> + @plugin=url_sig.so @pparam=<config file> @pparam=pristineurl The first simply enables this plugin for the rule. The second specifies the -location of the configuration file containing your signing keys. +location of the configuration file containing your signing keys. The third one, +if present, causes authentication to be performed on the original (pristine) URL +as received from the client. (The value of the parameter is not case sensitive.) For example, if we wanted to restrict all paths under a ``/download`` directory on our website ``foo.com`` we might have a remap line like this:: @@ -184,7 +186,7 @@ Key Index Parts Configures which components of the URL to use for signature verification. - The value of this paramerts is a string of ones and zeroes, each enabling + The value of this parameter is a string of ones and zeroes, each enabling or disabling the use of a URL part for signatures. The URL scheme (e.g. ``http://``) is never part of the signature. The first number of this parameter's value indicates whether to include the FQDN, and all remaining diff --git a/plugins/experimental/url_sig/sign.pl b/plugins/experimental/url_sig/sign.pl index 7f2cc7b..66fa729 100755 --- a/plugins/experimental/url_sig/sign.pl +++ b/plugins/experimental/url_sig/sign.pl @@ -48,7 +48,9 @@ if ( !defined($key) || !defined($url) || !defined($duration) || !defined($keyind exit(1); } -$url =~ s/^http:\/\///; +my $url_prefix = $url; +$url_prefix =~ s/^([^:]*:\/\/).*$/$1/; +$url =~ s/^[^:]+:\/\///; my $i = 0; my $part_active = 0; my $j = 0; @@ -97,13 +99,13 @@ else { if ($urlHasParams == -1) { my $qstring = ( split( /\?/, $string ) )[1]; - print "curl -s -o /dev/null -v --max-redirs 0 'http://" . $url . "?" . $qstring . $digest . "'\n"; + print "curl -s -o /dev/null -v --max-redirs 0 '" . $url_prefix . $url . "?" . $qstring . $digest . "'\n"; } else { my $url_noparams = ( split( /\?/, $url ) )[0]; my $qstring = ( split( /\?/, $string ) )[1]; - print "curl -s -o /dev/null -v --max-redirs 0 'http://" . $url_noparams . "?" . $qstring . $digest . "'\n"; + print "curl -s -o /dev/null -v --max-redirs 0 '" . $url_prefix . $url_noparams . "?" . $qstring . $digest . "'\n"; } sub help { diff --git a/plugins/experimental/url_sig/url_sig.c b/plugins/experimental/url_sig/url_sig.c index ad583c9..537ef35 100644 --- a/plugins/experimental/url_sig/url_sig.c +++ b/plugins/experimental/url_sig/url_sig.c @@ -38,6 +38,7 @@ #include <arpa/inet.h> #include <limits.h> #include <ctype.h> +#include <stdint.h> #ifdef HAVE_PCRE_PCRE_H #include <pcre/pcre.h> @@ -48,7 +49,7 @@ #include <ts/ts.h> #include <ts/remap.h> -static const char *PLUGIN_NAME = "url_sig"; +static const char PLUGIN_NAME[] = "url_sig"; struct config { TSHttpStatus err_status; @@ -56,6 +57,7 @@ struct config { char keys[MAX_KEY_NUM][MAX_KEY_LEN]; pcre *regex; pcre_extra *regex_extra; + int pristine_url_flag; }; static void @@ -83,12 +85,12 @@ 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)); + snprintf(errbuf, errbuf_size, "[tsremap_init] - Invalid TSRemapInterface argument"); 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, + snprintf(errbuf, errbuf_size, "[TSRemapInit] - Incorrect API version %ld.%ld", api_info->tsremap_version >> 16, (api_info->tsremap_version & 0xffff)); return TS_ERROR; } @@ -104,9 +106,11 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s char config_filepath_buf[PATH_MAX], *config_file; struct config *cfg; - if (argc != 3) { - snprintf(errbuf, errbuf_size - 1, - "[TSRemapNewKeyInstance] - Argument count wrong (%d)... Need exactly two pparam= (config file name)", argc); + if ((argc < 3) || (argc > 4)) { + snprintf(errbuf, errbuf_size, + "[TSRemapNewInstance] - Argument count wrong (%d)... config file path is required first pparam, \"pristineurl\" is" + "optional second pparam.", + argc); return TS_ERROR; } TSDebug(PLUGIN_NAME, "Initializing remap function of %s -> %s with config from %s", argv[0], argv[1], argv[2]); @@ -120,7 +124,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s 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); + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Error opening file %s", config_file); return TS_ERROR; } @@ -152,8 +156,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s *pos = '\0'; } if (pos == NULL || strlen(value) >= MAX_KEY_LEN) { - snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance] - Maximum key length (%d) exceeded on line %d", MAX_KEY_LEN - 1, - line_no); + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Maximum key length (%d) exceeded on line %d", MAX_KEY_LEN - 1, line_no); fclose(file); free_cfg(cfg); return TS_ERROR; @@ -170,12 +173,12 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s } TSDebug(PLUGIN_NAME, "key number %d == %s", keynum, value); if (keynum >= MAX_KEY_NUM || keynum < 0) { - snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance] - Key number (%d) >= MAX_KEY_NUM (%d) or NaN", keynum, MAX_KEY_NUM); + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Key number (%d) >= MAX_KEY_NUM (%d) or NaN", keynum, MAX_KEY_NUM); fclose(file); free_cfg(cfg); return TS_ERROR; } - strncpy(&cfg->keys[keynum][0], value, MAX_KEY_LEN - 1); + snprintf(&cfg->keys[keynum][0], MAX_KEY_LEN, "%s", value); } else if (strncmp(line, "error_url", 9) == 0) { if (atoi(value)) { cfg->err_status = atoi(value); @@ -214,32 +217,40 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_s } } + fclose(file); + + if (argc > 3) { + if (strcasecmp(argv[3], "pristineurl") == 0) { + cfg->pristine_url_flag = 1; + + } else { + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - second pparam (if present) must be pristineurl"); + free_cfg(cfg); + return TS_ERROR; + } + } + 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"); - fclose(file); + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Invalid config, err_status == 302, but err_url == NULL"); free_cfg(cfg); return TS_ERROR; } 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"); - fclose(file); + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Invalid config, err_status == 403, but err_url != NULL"); free_cfg(cfg); return TS_ERROR; } break; default: - snprintf(errbuf, errbuf_size - 1, "[TSRemapNewInstance] - Return code %d not supported", cfg->err_status); - fclose(file); + snprintf(errbuf, errbuf_size, "[TSRemapNewInstance] - Return code %d not supported", cfg->err_status); free_cfg(cfg); return TS_ERROR; } - fclose(file); - *ih = (void *)cfg; return TS_SUCCESS; } @@ -251,7 +262,7 @@ TSRemapDeleteInstance(void *ih) } static void -err_log(char *url, char *msg) +err_log(const char *url, const char *msg) { if (msg && url) { TSDebug(PLUGIN_NAME, "[URL=%s]: %s", url, msg); @@ -264,18 +275,18 @@ err_log(char *url, char *msg) // See the README. All Signing parameters must be concatenated to the end // of the url and any application query parameters. static char * -getAppQueryString(char *query_string, unsigned int query_length) +getAppQueryString(const char *query_string, int query_length) { int done = 0; char *p; - char buf[MAX_QUERY_LEN]; + char buf[MAX_QUERY_LEN + 1]; - if (query_length >= sizeof(buf)) { + if (query_length > MAX_QUERY_LEN) { TSDebug(PLUGIN_NAME, "Cannot process the query string as the length exceeds %d bytes", MAX_QUERY_LEN); return NULL; } - memset(buf, 0, MAX_QUERY_LEN); - strncpy(buf, query_string, min(query_length, sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + strncpy(buf, query_string, query_length); p = buf; TSDebug(PLUGIN_NAME, "query_string: %s, query_length: %d", query_string, query_length); @@ -289,7 +300,7 @@ getAppQueryString(char *query_string, unsigned int query_length) case 'P': case 'S': done = 1; - if (*(p - 1) == '&') { + if ((p > buf) && (*(p - 1) == '&')) { *(p - 1) = '\0'; } else { (*p = '\0'); @@ -317,13 +328,13 @@ getAppQueryString(char *query_string, unsigned int query_length) TSRemapStatus TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) { - struct config *cfg; - cfg = (struct config *)ih; + const struct config *cfg = (const struct config *)ih; - int url_len = 0; - time_t expiration = 0; - int algorithm = -1; - int keyindex = -1; + int url_len = 0; + int current_url_len = 0; + uint64_t expiration = 0; + int algorithm = -1; + int keyindex = -1; int cmp_res; int rval; unsigned int i = 0; @@ -331,7 +342,8 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) unsigned int sig_len = 0; /* all strings are locally allocated except url... about 25k per instance */ - char *url; + char *const current_url = TSUrlStringGet(rri->requestBufp, rri->requestUrl, ¤t_url_len); + const char *url = current_url; char signed_part[8192] = {'\0'}; // this initializes the whole array and is needed char urltokstr[8192] = {'\0'}; char client_ip[CIP_STRLEN] = {'\0'}; @@ -339,33 +351,42 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) 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, *app_qry = 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"); + if (current_url_len >= MAX_REQ_LEN - 1) { + err_log(current_url, "URL string too long."); goto deny; } + if (cfg->pristine_url_flag) { + TSMBuffer mbuf; + TSMLoc ul; + TSReturnCode rc = TSHttpTxnPristineUrlGet(txnp, &mbuf, &ul); + if (rc != TS_SUCCESS) { + TSError("[url_sig] Failed call to TSHttpTxnPristineUrlGet()"); + goto deny; + } + url = TSUrlStringGet(mbuf, ul, &url_len); + if (url_len >= MAX_REQ_LEN - 1) { + err_log(url, "Pristine URL string too long."); + goto deny; + } + + } else { + url_len = current_url_len; + } + TSDebug(PLUGIN_NAME, "%s", url); - query = strstr(url, "?"); + const char *query = strchr(url, '?'); if (cfg->regex) { int offset = 0, options = 0; int ovector[30]; - int len = url_len; - char *anchor = strstr(url, "#"); + int len = url_len; + const char *anchor = strchr(url, '#'); if (query && !anchor) { len -= (query - url); } else if (anchor && !query) { @@ -383,26 +404,26 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) 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) { + const char *cp = strstr(query, CIP_QSTRING "="); + if (cp != NULL) { + int len_cip_qstring = strlen(CIP_QSTRING); + cp += len_cip_qstring - 1; + const char *cpp = strchr(cp, '&'); + if (!cpp) { + err_log(url, "All required values after IP address string missing"); + goto deny; + } + if (!cpp || (cpp - cp) > CIP_STRLEN - 1 || (cpp - cp) < 4) { err_log(url, "IP address string too long or short"); goto deny; } - strncpy(client_ip, p + strlen(CIP_QSTRING) + 1, min((pp - p - (strlen(CIP_QSTRING) + 1)), sizeof(client_ip) - 1)); - client_ip[pp - p - (strlen(CIP_QSTRING) + 1)] = '\0'; + memcpy(client_ip, cp + len_cip_qstring + 1, (cpp - cp - (len_cip_qstring + 1))); + client_ip[cpp - cp - (len_cip_qstring + 1)] = '\0'; TSDebug(PLUGIN_NAME, "CIP: -%s-", client_ip); retval = TSHttpTxnClientFdGet(txnp, &sockfd); if (retval != TS_SUCCESS) { @@ -422,24 +443,23 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) } } // Expiration - p = strstr(query, EXP_QSTRING "="); - if (p != NULL) { - p += strlen(EXP_QSTRING) + 1; - expiration = atoi(p); - if (expiration == 0 || expiration < time(NULL)) { + cp = strstr(query, EXP_QSTRING "="); + if (cp != NULL) { + cp += strlen(EXP_QSTRING) + 1; + if (sscanf(cp, "%" SCNu64, &expiration) != 1 || (time_t)expiration < time(NULL)) { err_log(url, "Invalid expiration, or expired"); goto deny; } - TSDebug(PLUGIN_NAME, "Exp: %d", (int)expiration); + TSDebug(PLUGIN_NAME, "Exp: %" PRIu64, 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); + cp = strstr(query, ALG_QSTRING "="); + if (cp != NULL) { + cp += strlen(ALG_QSTRING) + 1; + algorithm = atoi(cp); // The check for a valid algorithm is later. TSDebug(PLUGIN_NAME, "Algorithm: %d", algorithm); } else { @@ -447,10 +467,10 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) goto deny; } // Key index - p = strstr(query, KIN_QSTRING "="); - if (p != NULL) { - p += strlen(KIN_QSTRING) + 1; - keyindex = atoi(p); + cp = strstr(query, KIN_QSTRING "="); + if (cp != NULL) { + cp += strlen(KIN_QSTRING) + 1; + keyindex = atoi(cp); if (keyindex < 0 || keyindex >= MAX_KEY_NUM || 0 == cfg->keys[keyindex][0]) { err_log(url, "Invalid key index"); goto deny; @@ -461,21 +481,27 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) 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); + const char *parts = NULL; + cp = strstr(query, PAR_QSTRING "="); + if (cp != NULL) { + cp += strlen(PAR_QSTRING) + 1; + parts = cp; // NOTE parts is not NULL terminated it is terminated by "&" of next param + cp = strchr(parts, '&'); + if (cp) { + TSDebug(PLUGIN_NAME, "Parts: %.*s", (int)(cp - parts), parts); + } else { + TSDebug(PLUGIN_NAME, "Parts: %s", 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 + const char *signature = NULL; + cp = strstr(query, SIG_QSTRING "="); + if (cp != NULL) { + cp += strlen(SIG_QSTRING) + 1; + signature = cp; 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)"); @@ -487,13 +513,20 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) } /* 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, + TSDebug(PLUGIN_NAME, "Found all needed parameters: C=%s E=%" PRIu64 " A=%d K=%d P=%s S=%s", client_ip, 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); + cp = strchr(url, '?'); + // Skip scheme and initial forward slashes. + const char *skip = strchr(url, ':'); + if (!skip || skip[1] != '/' || skip[2] != '/') { + goto deny; + } + skip += 3; + memcpy(urltokstr, skip, cp - skip); + char *strtok_r_p; + const char *part = strtok_r(urltokstr, "/", &strtok_r_p); while (part != NULL) { if (parts[j] == '1') { strncat(signed_part, part, sizeof(signed_part) - strlen(signed_part) - 1); @@ -503,12 +536,12 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) 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); + part = strtok_r(NULL, "/", &strtok_r_p); } signed_part[strlen(signed_part) - 1] = '?'; // chop off the last /, replace with '?' - p = strstr(query, SIG_QSTRING "="); - strncat(signed_part, query, min((p - query) + strlen(SIG_QSTRING) + 1, sizeof(signed_part) - strlen(signed_part) - 1)); + cp = strstr(query, SIG_QSTRING "="); + strncat(signed_part, query, (cp - query) + strlen(SIG_QSTRING) + 1); TSDebug(PLUGIN_NAME, "Signed string=\"%s\"", signed_part); @@ -556,7 +589,10 @@ TSRemapDoRemap(void *ih, TSHttpTxn txnp, TSRemapRequestInfo *rri) /* ********* Deny ********* */ deny: - TSfree(url); + if (url != current_url) { + TSfree((void *)url); + } + TSfree((void *)current_url); switch (cfg->err_status) { case TS_HTTP_STATUS_MOVED_TEMPORARILY: @@ -570,7 +606,7 @@ deny: rri->redirect = 1; break; default: - TSHttpTxnErrorBodySet(txnp, TSstrdup("Authorization Denied"), strlen("Authorization Denied") - 1, TSstrdup("text/plain")); + TSHttpTxnErrorBodySet(txnp, TSstrdup("Authorization Denied"), sizeof("Authorization Denied") - 1, TSstrdup("text/plain")); break; } /* Always set the return status */ @@ -580,15 +616,23 @@ deny: /* ********* Allow ********* */ allow: - if (query != NULL) { - app_qry = getAppQueryString(query, strlen(query)); + if (url != current_url) { + TSfree((void *)url); + } + + const char *current_query = strchr(current_url, '?'); + const char *app_qry = NULL; + if (current_query != NULL) { + current_query++; + app_qry = getAppQueryString(current_query, strlen(current_query)); } - TSfree(url); + TSfree((void *)current_url); + /* drop the query string so we can cache-hit */ if (app_qry != NULL) { rval = TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, app_qry, strlen(app_qry)); - TSfree(app_qry); + TSfree((void *)app_qry); } else { rval = TSUrlHttpQuerySet(rri->requestBufp, rri->requestUrl, NULL, 0); } diff --git a/tests/gold_tests/logging/ccid_ctid.test.py b/tests/gold_tests/logging/ccid_ctid.test.py index ab783e1..5236313 100644 --- a/tests/gold_tests/logging/ccid_ctid.test.py +++ b/tests/gold_tests/logging/ccid_ctid.test.py @@ -41,7 +41,7 @@ ts.addSSLfile("../remap/ssl/server.key") ts.Variables.ssl_port = 4443 ts.Disk.records_config.update({ - # 'proxy.config.diags.debug.enabled': '1', + # 'proxy.config.diags.debug.enabled': 1, 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), 'proxy.config.http.server_ports': 'ipv4:{0} ipv4:{1}:proto=http2;http:ssl'.format(ts.Variables.port, ts.Variables.ssl_port) diff --git a/tests/gold_tests/pluginTest/url_sig/run_sign.sh b/tests/gold_tests/pluginTest/url_sig/run_sign.sh new file mode 100755 index 0000000..fcc404c --- /dev/null +++ b/tests/gold_tests/pluginTest/url_sig/run_sign.sh @@ -0,0 +1,138 @@ +# Script to run sign.pl script. Single parameter is number 1 or greater selecting a set of script parameters. + +# 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. + +# Generate one or more sets of arguments for the sign.pl perl script. +# +cmd_args () +{ +if [[ "$1" = "" ]] ; then + SELECT=1 +else + SELECT="$1" +fi + +FOREVER="$((60 * 60 * 24 * 365 * 1000))" + +case "$SELECT" in +1) + echo "--url http://one.two.three/foo/abcde/qrstuvwxyz" + echo "--useparts 1" + echo "--algorithm 1" + echo "--duration $FOREVER" + echo "--keyindex 7" + echo "--key dqsgopTSM_doT6iAysasQVUKaPykyb6e" + ;; +2) + echo "--client 127.0.0.1" + echo "--url http://four.five.six/foo/abcde/qrstuvwxyz" + echo "--useparts 1" + echo "--algorithm 1" + echo "--duration $FOREVER" + echo "--keyindex 15" + echo "--key 9MuXIiZ70HPi_qhqfSgdu9oJHpcj9yaO" + ;; +3) + echo "--url http://seven.eight.nine/foo/abcde/qrstuvwxyz" + echo "--useparts 1" + echo "--algorithm 2" + echo "--duration $FOREVER" + echo "--keyindex 0" + echo "--key hV3wqyq1QxJeF76JkzHf93tuLYv_abw5" + ;; +4) + echo "--client 127.0.0.1" + echo "--url http://seven.eight.nine/foo/abcde/qrstuvwxyz" + echo "--useparts 010" + echo "--algorithm 2" + echo "--duration $FOREVER" + echo "--keyindex 13" + echo "--key CGRDwMO96_vRjFCfks6oxkeV7IdTnA6f" + ;; +5) + echo "--client 127.0.0.1" + echo "--url http://seven.eight.nine/foo/abcde/qrstuvwxyz" + echo "--useparts 101" + echo "--algorithm 2" + echo "--duration $FOREVER" + echo "--keyindex 13" + echo "--key CGRDwMO96_vRjFCfks6oxkeV7IdTnA6f" + ;; +*h*) + ;; +*) + echo "run_sign.sh: bad seletion parameter" 1>&2 + exit 1 + ;; +esac +} + +# Find the path to the sign.pl script in the url_sig (source) directory. +# +find_cmd () +{ +local D T='..' +while [[ ! -d $T/.git ]] +do + if [[ ! -d $T/.. ]] ; then + echo "Working directory not in a git repo" 1>&2 + exit 1 + fi + T="$T/.." +done + +for D in $( find $T -name url_sig -type d ) +do + if [[ -x $D/sign.pl ]] ; then + echo "$D/sign.pl" + return 0 + fi +done + +echo "cannot find sign.pl script" 1>&2 +exit 1 +} + +FOUND=N +echo "$PERL5LIB" | tr ':' ' ' | while read D +do + if [[ -f $D/Digest/HMAC_MD5.pm ]] ; then + FOUND=Y + break + fi +done + +if [[ $FOUND = N ]] ; then + P=$( find / 2>/dev/null | grep -F Digest/HMAC_MD5.pm | head -1 ) + if [[ ! -f $P ]] ; then + echo "Cannot find HMAC_MD5.pm" 1>&2 + exit 1 + fi + export PERL5LIB="$PERL5LIB:$( dirname $( dirname $P ) )" +fi + +CMD=$( find_cmd ) +if [[ "$?" != 0 ]] ; then + exit 1 +fi + +ARGS=$( cmd_args "$1" ) +if [[ "$?" != 0 ]] ; then + exit 1 +fi + +$CMD $ARGS | tr ' ' '\n' | tail -1 diff --git a/tests/gold_tests/pluginTest/url_sig/url_sig.config b/tests/gold_tests/pluginTest/url_sig/url_sig.config new file mode 100644 index 0000000..7c10a6b --- /dev/null +++ b/tests/gold_tests/pluginTest/url_sig/url_sig.config @@ -0,0 +1,17 @@ +key0 = hV3wqyq1QxJeF76JkzHf93tuLYv_abw5 +key1 = nIpyXbVqPFVN7y8yMlfgFBLnOqDSufMy +key2 = 4UED1ELmHkEcXrS_7yEYPKtgUZdGWaP2 +key3 = mv2vPGJpq2iFDbiV3dJG4ZqCAzRTIpTD +key4 = 2cnob1tuGEiYhwJLYRLa5bfyuZH1zI0S +key5 = poC7zK9IrDl3rljvuZ0bbMP3e5f0woKt +key6 = _k8diypYMebSCEEjYNszZbG906JZI6Bx +key7 = dqsgopTSM_doT6iAysasQVUKaPykyb6e +key8 = AzM3mhTDEkyJjyqQctv0NVxCL3FmXDzW +key9 = iRHQE9ucS44oAhdXmM148wMTJAO4XAVV +key10 = b1OMb39dGhMSg_wArQnvqGIBgQGFjnNl +key11 = YpA8qBkvohdamogQ4zTuoPw50PbezdL0 +key12 = 4Q4OCnY_gmcDuw5756Wk1XG7PEi24g1_ +key13 = CGRDwMO96_vRjFCfks6oxkeV7IdTnA6f +key14 = sXTWfNyHkN2SJ9eKifetPzfcg0_rNhXM +key15 = 9MuXIiZ70HPi_qhqfSgdu9oJHpcj9yaO +error_url = 403 diff --git a/tests/gold_tests/pluginTest/url_sig/url_sig.gold b/tests/gold_tests/pluginTest/url_sig/url_sig.gold new file mode 100644 index 0000000..35f62da --- /dev/null +++ b/tests/gold_tests/pluginTest/url_sig/url_sig.gold @@ -0,0 +1,24 @@ +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 403 Forbidden +Authorization Denied* Trying 127.0.0.1... +< HTTP/1.1 200 OK +< HTTP/1.1 200 OK +< HTTP/1.1 200 OK +< HTTP/1.1 200 OK +< HTTP/1.1 200 OK +< HTTP/1.1 200 OK diff --git a/tests/gold_tests/pluginTest/url_sig/url_sig.test.py b/tests/gold_tests/pluginTest/url_sig/url_sig.test.py new file mode 100644 index 0000000..78b0228 --- /dev/null +++ b/tests/gold_tests/pluginTest/url_sig/url_sig.test.py @@ -0,0 +1,261 @@ +''' +''' +# 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. + +import os +import subprocess +Test.Summary = ''' +Test url_sig plugin +''' + +Test.SkipUnless( + Condition.HasATSFeature('TS_USE_TLS_ALPN'), +) + +# Skip if plugins not present. +Test.SkipUnless(Condition.PluginExists('url_sig.so')) +Test.SkipUnless(Condition.PluginExists('balancer.so')) + +# Set up to check the output after the tests have run. +# +url_sig_log_id = Test.Disk.File("url_sig_short.log") +url_sig_log_id.Content = "url_sig.gold" + +server = Test.MakeOriginServer("server") + +request_header = { + "headers": "GET /foo/abcde/qrstuvwxyz HTTP/1.1\r\nHost: just.any.thing\r\n\r\n", "timestamp": "1469733493.993", "body": "" +} +# expected response from the origin server +response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""} +# add response to the server dictionary +server.addResponse("sessionfile.log", request_header, response_header) + +# Define default ATS +ts = Test.MakeATSProcess("ts", select_ports=False) + +ts.addSSLfile("../../remap/ssl/server.pem") +ts.addSSLfile("../../remap/ssl/server.key") + +ts.Variables.ssl_port = 4443 + +ts.Disk.records_config.update({ + # 'proxy.config.diags.debug.enabled': 1, + # 'proxy.config.diags.debug.tags': 'http|url_sig', + 'proxy.config.http.cache.http': 0, # Make sure each request is forwarded to the origin server. + 'proxy.config.proxy_name': 'Poxy_Proxy', # This will be the server name. + 'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir), + 'proxy.config.http.server_ports': ( + 'ipv4:{0} ipv4:{1}:proto=http:ssl'.format(ts.Variables.port, ts.Variables.ssl_port)) +}) + +ts.Disk.ssl_multicert_config.AddLine( + 'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key' +) + +# Use unchanged incoming URL. +# +ts.Disk.remap_config.AddLine( + 'map http://one.two.three/ http://127.0.0.1:{}/'.format(server.Variables.Port) + + ' @plugin=url_sig.so @pparam={}/url_sig.config'.format(Test.TestDirectory) +) + +# Use unchanged incoming HTTPS URL. +# +ts.Disk.remap_config.AddLine( + 'map https://one.two.three/ http://127.0.0.1:{}/'.format(server.Variables.Port) + + ' @plugin=url_sig.so @pparam={}/url_sig.config'.format(Test.TestDirectory) +) + +# Use pristine URL, incoming URL unchanged. +# +ts.Disk.remap_config.AddLine( + 'map http://four.five.six/ http://127.0.0.1:{}/'.format(server.Variables.Port) + + ' @plugin=url_sig.so @pparam={}/url_sig.config @pparam=pristineurl'.format(Test.TestDirectory) +) + +# Use pristine URL, incoming URL changed. +# +ts.Disk.remap_config.AddLine( + 'map http://seven.eight.nine/ http://dummy' + + ' @plugin=balancer.so @pparam=--policy=hash,url @pparam=127.0.0.1:{}'.format(server.Variables.Port) + + ' @plugin=url_sig.so @pparam={}/url_sig.config @pparam=PristineUrl'.format(Test.TestDirectory) +) + +# Ask the OS if the port is ready for connect() +# +def CheckPort(Port): + return lambda: 0 == subprocess.call('netstat --listen --tcp -n | grep -q :{}'.format(Port), shell=True) + +# Validation failure tests. + +LogTee = " 2>&1 | tee -a {}/url_sig_long.log".format(Test.RunDirectory) + +# Bad client / MD5 / P=101 / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.StartBefore(ts, ready=CheckPort(ts.Variables.ssl_port)) +tr.Processes.Default.StartBefore(server, ready=CheckPort(server.Variables.Port)) +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.2&E=33046620008&A=2&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# With client / MD5 / P=010 / URL pristine / URL altered -- Expired. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=1&A=2&K=13&P=010&S=f237aad1fa010234d7bf8108a0e36387'" + + LogTee +) + +# With client / No algorithm / P=101 / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# With client / Bad algorithm / P=101 / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=3&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# With client / MD5 / No parts / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&S=d1f352d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# With client / MD5 / P=10 (bad) / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=10&S=d1f352d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# With client / MD5 / P=101 / URL pristine / URL altered -- No signature. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=101'" + + LogTee +) + +# With client / MD5 / P=101 / URL pristine / URL altered -- Bad signature. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=101&S=d1f452d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# With client / MD5 / P=101 / URL pristine / URL altered -- Spurious &. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8#'" + + LogTee +) + +# Success tests. + +# No client / SHA1 / P=1 / URL not pristine / URL not altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://one.two.three/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?E=33046618506&A=1&K=7&P=1&S=acae22b0e1ba6ea6fbb5d26018dbf152558e98cb'" + + LogTee +) + +# With client / SHA1 / P=1 / URL pristine / URL not altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://four.five.six/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046618556&A=1&K=15&P=1&S=f4103561a23adab7723a89b9831d77e0afb61d92'" + + LogTee +) + +# No client / MD5 / P=1 / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?E=33046618586&A=2&K=0&P=1&S=0364efa28afe345544596705b92d20ac'" + + LogTee +) + +# With client / MD5 / P=010 / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046619717&A=2&K=13&P=010&S=f237aad1fa010234d7bf8108a0e36387'" + + LogTee +) + +# With client / MD5 / P=101 / URL pristine / URL altered. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --proxy http://127.0.0.1:{} 'http://seven.eight.nine/".format(ts.Variables.port) + + "foo/abcde/qrstuvwxyz?C=127.0.0.1&E=33046620008&A=2&K=13&P=101&S=d1f352d4f1d931ad2f441013402d93f8'" + + LogTee +) + +# No client / SHA1 / P=1 / URL not pristine / URL not altered -- HTTPS. +# +tr = Test.AddTestRun() +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.Command = ( + "curl --verbose --http1.1 --insecure --header 'Host: one.two.three' 'https://127.0.0.1:{}/".format(ts.Variables.ssl_port) + + "foo/abcde/qrstuvwxyz?E=33046618506&A=1&K=7&P=1&S=acae22b0e1ba6ea6fbb5d26018dbf152558e98cb'" + + LogTee + " ; grep -F -e '< HTTP' -e Authorization {0}/url_sig_long.log > {0}/url_sig_short.log ".format(ts.RunDirectory) +) -- To stop receiving notification emails like this one, please contact ['"commits@trafficserver.apache.org" <commits@trafficserver.apache.org>'].