THRIFT-4084: Add a SSL/TLS negotiation check to crossfeature to verify SSLv3 is not active and that at least one of TLSv1.0 through 1.2 are accepted.
Client: csharp, d, go, nodejs, perl

This closes #1197
diff --git a/test/cpp/src/TestClient.cpp b/test/cpp/src/TestClient.cpp
index da20b89..a918bfb 100644
--- a/test/cpp/src/TestClient.cpp
+++ b/test/cpp/src/TestClient.cpp
@@ -136,8 +136,11 @@
   int ERR_EXCEPTIONS = 8;
   int ERR_UNKNOWN = 64;
 
-  string testDir = boost::filesystem::system_complete(argv[0]).parent_path().parent_path().parent_path().string();
-  string pemPath = testDir + "/keys/CA.pem";
+  string testDir  = boost::filesystem::system_complete(argv[0]).parent_path().parent_path().parent_path().string();
+  string caPath   = testDir + "/keys/CA.pem";
+  string certPath = testDir + "/keys/client.crt";
+  string keyPath  = testDir + "/keys/client.key";
+
 #if _WIN32
   transport::TWinsockSingleton::create();
 #endif
@@ -232,9 +235,15 @@
   boost::shared_ptr<TSSLSocketFactory> factory;
 
   if (ssl) {
+    cout << "Client Certificate File: " << certPath << endl;
+    cout << "Client Key         File: " << keyPath << endl;
+    cout << "CA                 File: " << caPath << endl;
+
     factory = boost::shared_ptr<TSSLSocketFactory>(new TSSLSocketFactory());
     factory->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
-    factory->loadTrustedCertificates(pemPath.c_str());
+    factory->loadTrustedCertificates(caPath.c_str());
+    factory->loadCertificate(certPath.c_str());
+    factory->loadPrivateKey(keyPath.c_str());
     factory->authenticate(true);
     socket = factory->createSocket(host, port);
   } else {
diff --git a/test/features/Makefile.am b/test/features/Makefile.am
index f8d6538..337d789 100644
--- a/test/features/Makefile.am
+++ b/test/features/Makefile.am
@@ -21,8 +21,10 @@
 	index.html \
 	known_failures_Linux.json \
 	Makefile.am \
+        nosslv3.sh \
 	string_limit.py \
 	tests.json \
 	theader_binary.py \
 	setup.cfg \
+        tls.sh \
 	util.py
diff --git a/test/features/nosslv3.sh b/test/features/nosslv3.sh
new file mode 100755
index 0000000..e550d51
--- /dev/null
+++ b/test/features/nosslv3.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+#
+# Checks to make sure SSLv3 is not allowed by a server.
+#
+
+THRIFTHOST=localhost
+THRIFTPORT=9090
+
+while [[ $# -ge 1 ]]; do
+  arg="$1"
+  argIN=(${arg//=/ })
+
+  case ${argIN[0]} in
+    -h|--host)
+    THRIFTHOST=${argIN[1]}
+    shift # past argument
+    ;;
+    -p|--port)
+    THRIFTPORT=${argIN[1]}
+    shift # past argument
+    ;;
+    *)
+          # unknown option ignored
+    ;;
+  esac
+
+  shift   # past argument or value
+done
+
+function nosslv3
+{
+  local nego
+  local negodenied
+
+  # echo "openssl s_client -connect $THRIFTHOST:$THRIFTPORT -CAfile ../keys/CA.pem -ssl3 2>&1 < /dev/null"
+  nego=$(openssl s_client -connect $THRIFTHOST:$THRIFTPORT -CAfile ../keys/CA.pem -ssl3 2>&1 < /dev/null)
+  negodenied=$?
+
+  if [[ $negodenied -ne 0 ]]; then
+    echo "[pass] SSLv3 negotiation disabled"
+    echo $nego
+    return 0
+  fi
+
+  echo "[fail] SSLv3 negotiation enabled!  stdout:"
+  echo $nego
+  return 1
+}
+
+nosslv3
+exit $?
diff --git a/test/features/tests.json b/test/features/tests.json
index cfcb4b6..3ab3b68 100644
--- a/test/features/tests.json
+++ b/test/features/tests.json
@@ -90,5 +90,27 @@
     "transports": ["buffered"],
     "sockets": ["ip"],
     "workdir": "features"
+  },
+  {
+    "name": "nosslv3",
+    "comment": "check to make sure SSLv3 is not supported",
+    "command": [
+      "nosslv3.sh"
+    ],
+    "protocols": ["binary"],
+    "transports": ["buffered"],
+    "sockets": ["ip-ssl"],
+    "workdir": "features"
+  },
+  {
+    "name": "tls",
+    "comment": "check to make sure TLSv1.0 or later is supported",
+    "command": [
+      "tls.sh"
+    ],
+    "protocols": ["binary"],
+    "transports": ["buffered"],
+    "sockets": ["ip-ssl"],
+    "workdir": "features"
   }
 ]
diff --git a/test/features/tls.sh b/test/features/tls.sh
new file mode 100755
index 0000000..dada6ab
--- /dev/null
+++ b/test/features/tls.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+#
+# Checks to make sure TLSv1.0 or later is allowed by a server.
+#
+
+THRIFTHOST=localhost
+THRIFTPORT=9090
+
+while [[ $# -ge 1 ]]; do
+  arg="$1"
+  argIN=(${arg//=/ })
+
+  case ${argIN[0]} in
+    -h|--host)
+    THRIFTHOST=${argIN[1]}
+    shift # past argument
+    ;;
+    -p|--port)
+    THRIFTPORT=${argIN[1]}
+    shift # past argument
+    ;;
+    *)
+          # unknown option ignored
+    ;;
+  esac
+
+  shift   # past argument or value
+done
+
+declare -A EXPECT_NEGOTIATE
+EXPECT_NEGOTIATE[tls1]=1
+EXPECT_NEGOTIATE[tls1_1]=1
+EXPECT_NEGOTIATE[tls1_2]=1
+
+failures=0
+
+function tls
+{
+  for PROTO in "${!EXPECT_NEGOTIATE[@]}"; do
+
+    local nego
+    local negodenied
+    local res
+
+    echo "openssl s_client -connect $THRIFTHOST:$THRIFTPORT -CAfile ../keys/CA.pem -$PROTO 2>&1 < /dev/null"
+    nego=$(openssl s_client -connect $THRIFTHOST:$THRIFTPORT -CAfile ../keys/CA.pem -$PROTO 2>&1 < /dev/null)
+    negodenied=$?
+    echo "result of command: $negodenied"
+
+    res="enabled"; if [[ ${EXPECT_NEGOTIATE[$PROTO]} -eq 0 ]]; then res="disabled"; fi
+
+    if [[ $negodenied -ne ${EXPECT_NEGOTIATE[$PROTO]} ]]; then
+      echo "$PROTO negotiation allowed"
+    else
+      echo "[warn] $PROTO negotiation did not work"
+      echo $nego
+      ((failures++))
+    fi
+  done
+}
+
+tls
+
+if [[ $failures -eq 3 ]]; then
+  echo "[fail] At least one of TLSv1.0, TLSv1.1, or TLSv1.2 needs to work, but does not"
+  exit $failures
+fi
+
+echo "[pass] At least one of TLSv1.0, TLSv1.1, or TLSv1.2 worked"
+exit 0
diff --git a/test/known_failures_Linux.json b/test/known_failures_Linux.json
index d60997d..5ca6d4e 100644
--- a/test/known_failures_Linux.json
+++ b/test/known_failures_Linux.json
@@ -47,6 +47,9 @@
   "csharp-d_binary_buffered-ip-ssl",
   "csharp-d_compact_buffered-ip-ssl",
   "csharp-d_json_buffered-ip-ssl",
+  "csharp-d_binary_framed-ip-ssl",
+  "csharp-d_compact_framed-ip-ssl",
+  "csharp-d_json_framed-ip-ssl",
   "csharp-erl_binary_buffered-ip-ssl",
   "csharp-erl_binary_framed-ip-ssl",
   "csharp-erl_compact_buffered-ip-ssl",
@@ -105,6 +108,7 @@
   "d-cpp_json_http-ip-ssl",
   "d-d_binary_http-ip",
   "d-d_compact_http-ip",
+  "d-d_json_http-ip",
   "d-dart_binary_framed-ip",
   "d-dart_binary_http-ip",
   "d-dart_compact_framed-ip",
@@ -220,9 +224,5 @@
   "hs-py3_json_framed-ip",
   "java-d_compact_buffered-ip",
   "java-d_compact_buffered-ip-ssl",
-  "java-d_compact_framed-ip",
-  "perl-erl_binary_buffered-ip-ssl",
-  "perl-erl_binary_framed-ip-ssl",
-  "perl-perl_binary_buffered-ip-ssl",
-  "perl-perl_binary_framed-ip-ssl"
+  "java-d_compact_framed-ip"
 ]
diff --git a/test/tests.json b/test/tests.json
index e228928..f1d6a47 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -121,8 +121,8 @@
       "framed:fastframed"
     ],
     "sockets": [
-      "ip-ssl",
-      "ip"
+      "ip",
+      "ip-ssl"
     ],
     "protocols": [
       "compact",
@@ -415,7 +415,7 @@
         "-I../../lib/perl/lib/",
         "TestClient.pl",
         "--ca=../keys/CA.pem",
-        "--cert=../keys/client.pem",
+        "--cert=../keys/client.crt",
         "--key=../keys/client.key"
       ]
     },
@@ -425,8 +425,7 @@
         "-Igen-perl/",
         "-I../../lib/perl/lib/",
         "TestServer.pl",
-        "--ca=../keys/CA.pem",
-        "--cert=../keys/server.pem",
+        "--cert=../keys/server.crt",
         "--key=../keys/server.key"
       ]
     },