1# 2# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"). 5# You may not use this file except in compliance with the License. 6# A copy of the License is located at 7# 8# http://aws.amazon.com/apache2.0 9# 10# or in the "license" file accompanying this file. This file is distributed 11# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12# express or implied. See the License for the specific language governing 13# permissions and limitations under the License. 14# 15import argparse 16import os 17import sys 18import subprocess 19import itertools 20import multiprocessing 21import json 22import time 23from pprint import pprint 24from os import environ 25from multiprocessing.pool import ThreadPool 26from s2n_test_constants import * 27 28 29def cleanup_processes(*processes): 30 for p in processes: 31 p.kill() 32 p.wait() 33 34def run_sslyze_scan(endpoint, port, scan_output_location, enter_fips_mode=False): 35 """ 36 Run SSLyze scan against s2nd listening on `endpoint` and `port` 37 38 :param int endpoint: endpoint for s2nd to listen on 39 :param int port: port for s2nd to listen on 40 :param str scan_output_location: Path and Filename of where to output JSON Results file 41 :param bool enter_fips_mode: True if s2nd should enter libcrypto's FIPS mode. Libcrypto must be built with a FIPS module to enter FIPS mode. 42 :return: 0 on successfully negotiation(s), -1 on failure 43 """ 44 45 s2nd_cmd = ["../../bin/s2nd"] 46 s2nd_cmd.extend([str(endpoint), str(port), "-n", "-s", "--parallelize"]) 47 48 s2nd_ciphers = "test_all_tls12" 49 if enter_fips_mode == True: 50 s2nd_ciphers = "test_all_fips" 51 s2nd_cmd.append("--enter-fips-mode") 52 s2nd_cmd.append("--ciphers") 53 s2nd_cmd.append(s2nd_ciphers) 54 55 # Run s2nd in the background 56 s2nd = subprocess.Popen(s2nd_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 57 58 59 sslyze_cmd = ["sslyze"] 60 sslyze_cmd.extend(["--robot", str(str(endpoint) + ":" + str(port)), str("--json_out=" + scan_output_location)]) 61 62 sslyze = subprocess.Popen(sslyze_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 63 sslyze.wait(timeout=(300 * 1000)) 64 65 #cleanup 66 cleanup_processes(s2nd, sslyze) 67 68 return 0 69 70def print_result(result_prefix, return_code): 71 suffix = "" 72 if return_code == 0: 73 if sys.stdout.isatty(): 74 suffix = "\033[32;1mPASSED\033[0m" 75 else: 76 suffix = "PASSED" 77 else: 78 if sys.stdout.isatty(): 79 suffix = "\033[31;1mFAILED\033[0m" 80 else: 81 suffix ="FAILED" 82 83 print(result_prefix + suffix) 84 85def check_sslyze_results(scan_output_location): 86 json_obj = json.load(open(scan_output_location)) 87 scan_time = json_obj["total_scan_time"] 88 robot_result = json_obj["accepted_targets"][0]["commands_results"]["robot"] 89 90 if("error_message" in robot_result): 91 print_result("SSLyze Error: " + robot_result["error_message"] + " ", 1) 92 return 1 93 94 failures = 0 95 robot_attack_failure = 0 96 97 if(robot_result["robot_result_enum"] != "NOT_VULNERABLE_NO_ORACLE"): 98 robot_attack_failure = 1 99 failures += 1 100 101 print_result("ROBOT Attack Regression Test... ", robot_attack_failure) 102 103 print("\nSSLyze Results Location: " + scan_output_location) 104 print("SSLyze Scan Time: %0.2f seconds\n" % float(scan_time)) 105 106 return failures 107 108def run_sslyze_test(host, port, fips_mode): 109 seconds_since_epoch = str(int(time.time())) 110 scan_output_location = "/tmp/sslyze_output_%s.json" % seconds_since_epoch 111 112 run_sslyze_scan(host, port, scan_output_location, fips_mode) 113 failed = check_sslyze_results(scan_output_location) 114 115 os.remove(scan_output_location) 116 return failed 117 118 119def main(): 120 parser = argparse.ArgumentParser(description='Runs SSLyze scan against s2nd') 121 parser.add_argument('host', help='The host for s2nd to bind to') 122 parser.add_argument('port', type=int, help='The port for s2nd to bind to') 123 parser.add_argument('--libcrypto', default='openssl-1.1.1', choices=S2N_LIBCRYPTO_CHOICES, 124 help="""The Libcrypto that s2n was built with. s2n supports different cipher suites depending on 125 libcrypto version. Defaults to openssl-1.1.1.""") 126 args = parser.parse_args() 127 128 # Retrieve the test ciphers to use based on the libcrypto version s2n was built with 129 host = args.host 130 port = args.port 131 132 fips_mode = False 133 if environ.get("S2N_TEST_IN_FIPS_MODE") is not None: 134 fips_mode = True 135 print("\n\tRunning s2nd in FIPS mode.") 136 137 print("\n\tRunning SSLyze tests with: " + os.popen('openssl version').read()) 138 139 return run_sslyze_test(host, port, fips_mode) 140 141 142if __name__ == "__main__": 143 sys.exit(main()) 144