#!/bin/bash
# -*- mode: sh; fill-column: 75; tab-width: 8; coding: utf-8-unix -*-

prog=`basename $0 .bash`
PREFIX=/usr/local
ROLE=toxcore
export PATH=/sbin:$PATH

[ -f /usr/local/etc/testforge/testforge.bash ] && \
    . /usr/local/etc/testforge/testforge.bash
#[ -n "$TESTF_VAR_LOCAL" ] && PREFIX=$TESTF_VAR_LOCAL
. $PREFIX/bin/usr_local_tput.bash || exit 2
. /usr/local/bin/proxy_ping_lib.bash >/dev/null  || \
    { ERROR loading /usr/local/bin/proxy_ping_lib.bash ; exit 3; }


#? . $PREFIX/src/usr_local_src.bash || exit 2

DNS_TRIES=3
LOGP=TestSSL_`date -u +%y-%m-%d_%H_$$`
rm -f $TMPDIR/${LOGP}*

#  analyze-ssl passed files.pythonhosted.org
# INFO: 226s analyze-ssl no error = /tmp/_files.pythonhosted.org_analyze-ssl.out
[ -z "$SSLTEST_TESTS" ] && SSLTEST_TESTS="curl openssl testssl nmap" #  sslscan
[ -z "$SSLTEST_CERTS" ] && SSLTEST_CERTS="/etc/ssl/certs/ca-certificates.crt /usr/local/etc/ssl/cacert-testforge.pem"
[ -z "$SSLTEST_TIMEOUT" ] && SSLTEST_TIMEOUT=30

[ -z "$SSLTEST_SOCKS_PROXY" -a -n "$socks_proxy" ] && SSLTEST_SOCKS_PROXY=$socks_proxy \
    && DBUG SSLTEST_SOCKS_PROXY=$socks_proxy
if [ -z "$SSLTEST_HTTPS_PROXY" -a -n "$https_proxy" ] ; then
    SSLTEST_HTTPS_PROXY=$https_proxy
    DBUG SSLTEST_HTTPS_PROXY=$SSLTEST_HTTPS_PROXY
fi
[ -z "$SSLTEST_HTTP_PROXY" -a -n "$http_proxy" ] && SSLTEST_HTTP_PROXY=$http_proxy \
    && DBUG SSLTEST_HTTP_PROXY=$http_proxy
[ -z "$BOX_BYPASS_PROXY_GROUP" ] && BOX_BYPASS_PROXY_GROUP=bin

SSL_LIB=openssl

# [ "$MODE" ] && proxy_ping_test.bash $MODE

declare -a BADSSL_SITES
BADSSL_SITES=(
    self-signed.badssl.com
    expired.badssl.com
    mixed.badssl.com
    rc4.badssl.com
    hsts.badssl.com
)
declare -a GOODSSL_SITES
GOODSSL_SITES=(
    files.pythonhosted.org
    mirrors.dotsrc.org
    deb.devuan.org
#    dfw.source.kernel.org
#    cdn.kernel.org
)

badssl=0
goodssl=0
[ "$#" -eq 0 ] && goodssl=1
tests="$SSLTEST_TESTS"
verbosity=2
outdir=/tmp
timeout=$SSLTEST_TIMEOUT
onion=0
CAFILE=/usr/local/etc/ssl/cacert-testforge.pem
TMPDIR=/tmp
SSL_PORT=443
SSL_VER=3

usage() {
    echo "Usage: $0 [OPTIONS] dirs-or-files"
    echo
    echo " -B | --badssl                 - test badssl.org sites"
    echo " -G | --goodssl                - test good sites"
    echo " -S | --ssl                    - tls version v1.x - 2 or 3"
    echo " -O | --onion                    - onion"
    echo " -o | --outdir=$TMPDIR            - output directory"
    echo " -v | --verbosity=$verbosity            - verbosity 0 least 5 most"
    echo " -T | --timeout=$timeout             - timeout in sec."
    echo " -E | --tests=`sed -e 's/ /,/g' <<< $tests`       - tests, comma separated"
    echo " -C | --certs=`sed -e 's/ /,/g' <<< $SSLTEST_CERTS`       - tests, comma separated"
    echo " -Y | --ciphers                - comma sep list of ciphers"
    echo " -P | --port                   - port default $SSL_PORT"
    echo " -N | --connect                - connect"
    echo
    echo " -V | --version                - print version of this script"
    echo " -h | --help                   - print this help"
}

SHORTOPTS="hVGBv:T:C:P:S:E:Y:ON:"
LONGOPTS="help,version:,goodssl,badssl,verbosity:,timeout,certs:,port:,ssl:,tests:,ciphers:,onion,connect:"
declare -a SITES
SITES=()

ARGS=$(getopt --options $SHORTOPTS  --longoptions $LONGOPTS -- "$@")
[ $? != 0 ] &&  { ERROR "error parsing getopt" ; exit 4 ; }

eval set -- "$ARGS"

while true; do
    case "$1" in
        -o|--outdir)
            shift
            TMPDIR="$1"
            ;;
        -v|--verbosity)
            shift
            verbosity="$1"
            ;;
        -T|--timeout)
            shift
            timeout="$1"
            ;;
        -S|--ssl)
            shift
            SSL_VER="$1"
            ;;
        -P|--port)
            shift
            SSL_PORT="$1"
            ;;
        -N|--connect)
            shift
            SSL_CONNECT="$1"
            ;;
        -C|--certs)
            shift
            SSLTEST_CERTS="`sed  -e 's/,/ /g' <<< $1`"
            ;;
        -Y|--ciphers)
            shift
            SSLTEST_CIPHERS="`sed  -e 's/,/ /g' <<< $1`"
            ;;
        -t|--tests)
            shift
            tests="`sed  -e 's/,/ /g' <<< $1`"
            ;;
        -O|--onion)
	    onion=1
	    ;;
        -G|--goodssl)
	    goodssl=1
	    badssl=0
	    ;;
        -B|--badssl)
	    badssl=1
	    goodssl=0
	    ;;
        -V|--version)
            usage
            exit 0
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        '--')
            shift
            SITES=("$@")
            break
            ;;
        *)
	    { ERROR "unrecognized arguments $*" ; exit 5 ; }
            break
            ;;
    esac
    shift
done

[ "${#SITES[*]}" -eq 0 -a $badssl -gt 0 ]  && SITES=("${BADSSL_SITES[@]}")
[ "${#SITES[*]}" -eq 0 -a $goodssl -gt 0 ] && SITES=("${GOODSSL_SITES[@]}")
[ "${#SITES[@]}" -eq 0 ] && { ERROR "no arguments $*" ; exit 7 ; }

[ "$SSL_VER" -ge 2 -a "$SSL_VER" -le 3 ] || { ERROR "SSL_VER $SSL_VER" ; exit 6 ; }
[ -d "$TMPDIR" ] || mkdir -p "$TMPDIR" || { ERROR "mkdir $TMPDIR" ; exit 8 ; }
[ -f $CAFILE ] || { ERROR "CAfile not found $CAFILE" ; exit 9 ; }

[ $onion -eq 0 ] && TIMEOUT=$timeout || TIMEOUT=`expr $timeout \* 2`
SSLTEST_TESTS="$tests"
declare -a tests_ran
tests_ran=()

grep -q "^wlan[1-9][ 	]00000000" /proc/net/route || { WARN "not connected" ; exit 0 ; }

IF=`route | grep ^def |sed -e 's/.* //'`
[ -n "$IF" ] || { ERROR "no IF" ; exit 10 ; }

IP=`ifconfig $IF|grep -A 2 ^wlan |grep inet | sed -e   's/.*inet //' -e 's/ .*//'`
[ -n "$IP" ] || { ERROR "no IP" ; exit 11 ; }

[ -z "$socks_proxy" ] || . /usr/local/bin/proxy_export.bash

netstat -nle4 | grep -v grep | grep -q 0.1:53 || \
    { WARN "DNS not running - netstat " ; }

# iptables-legacy-save | grep "OUTPUT -o wlan4 -m owner --gid-owner 2 -j ACCEPT"

# uses TIMEOUT=30
. $PREFIX/bin/testforge_ssl_lib.bash

if [ "$USER" = bin ] ; then
    [ -z "$SOCKS_HOST" ] && SOCKS_HOST=
    [ -z "$SOCKS_PORT" ] && SOCKS_PORT=
    [ -z "$SOCKS_DNS" ]  && SOCKS_DNS=9053
else
    DEBUG=0 proxy_ping_get_socks >/dev/null
    [ -z "$SOCKS_HOST" ] && SOCKS_HOST=127.0.0.1
    [ -z "$SOCKS_PORT" ] && SOCKS_PORT=9050
    [ -z "$SOCKS_DNS" ]  && SOCKS_DNS=9053
fi

if [ "$USER" = bin ] ; then
    TORSOCKS=""
elif [ $SOCKS_HOST != 127.0.0.1 ] ; then
    TORSOCKS="torsocks --address $SOCKS_HOST --port $SOCKS_PORT "
elif [ $SOCKS_PORT != 9050 ] ; then
    TORSOCKS="torsocks --port $SOCKS_PORT "
else
    TORSOCKS="torsocks "
fi

if [ -n "$SSLTEST_HTTPS_PROXY" ] ; then
    grep -q "SocksPolicy *accept *$IP" /etc/tor/torrc || \
	{ WARN "need SocksPolicy accept $IP in /etc/tor/torrc" ; }
fi

# This works off the $https_proxy environment variable in the form http://127.0.0.1:9128
# so you can test trans routing by call this with that unset.
ssltest_proxies $onion

rm -f $TMPDIR/${LOGP}.*.*
OUTF=$TMPDIR/${LOGP}.out
for CAFILE in $SSLTEST_CERTS ; do
    grep -q "^wlan[1-9][ 	]00000000" /proc/net/route || {
	WARN $prog we are not connected >&2
	exit `expr 256 - 1`
    }

    [  -f $CAFILE ] || { ERROR "CAfile not found $CAFILE" ; continue ; }
    DATE DBUG CAFILE=$CAFILE --address $SOCKS_HOST --port $SOCKS_PORT

    cacert=`basename $CAFILE`
    for site in "${SITES[@]##*/}" ; do
        warns=0
	IF=`route | grep ^def |sed -e 's/.* //'`
	[ -n "$IF" ] || { WARN "$site no route" ; continue ; }

	SITE_OUTF=$TMPDIR/${LOGP}_${site}.out
	DEBUG=1 DATE DBUG $site CAFILE=$CAFILE $SITE_OUTF | tee -a $SITE_OUTF

	# ERROR: Could not resolve hostname www.devuan.org.
        i=0
	while [ $i -le $DNS_TRIES ] ; do
	        if [ $onion -eq 0 ] ; then
  		    site_ip=`dig $site +retry=5 +tries=2 +noall +answer +short | awk '{ print $1 }'` && break
		else
  		    site_ip=`tor-resolve -4 $site` && break
		  fi
	 	i=`expr $i + 1`
		sleep 5
            done
            [ $i -ge $DNS_TRIES ] && ERROR failed resolve $site | tee -a $SITE_OUTF
	    [ $i -ge $DNS_TRIES ] && site_ip=$site

	elt=sslscan
	SSLSCAN_ELTS="$SSLSCAN_ARGS --certs $CAFILE --sni-name $site"
	[[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	    tests_ran+=($elt) && \
	  ssltest_sslscan $elt $site $SITE_OUTF $site_ip

    	elt=openssl
	OPENSSL_ELTS="$OPENSSL_ARGS -CAfile $CAFILE -servername $site"
	[ -n "$SSL_CONNECT" ] && OPENSSL_ELTS="$OPENSSL_ARGS -connect ${SSL_CONNECT}:$SSL_PORT"
	[[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	    [ $onion -eq 0 ] && \
	    tests_ran+=($elt) && \
	  ssltest_openssl $elt $site $SITE_OUTF $site_ip

	elt=testssl
	rm -f $TMPDIR/${LOGP}.$site.$elt.json # --jsonfile-pretty $TMPDIR/${LOGP}.$site.$elt.json
	TESTSSL_ELTS="$TESTSSL_ARGS --add-ca $CAFILE --append --ip $site_ip"
	[[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	    [ $onion -eq 0 ] && \
	    tests_ran+=($elt) && \
	  ssltest_testssl $elt $site $SITE_OUTF $site_ip

        elt=analyze-ssl
	ANALYZE_ELTS="$ANALYZE_ARGS --CApath $CAFILE --name $site"
	[[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	  [ $SSL_PORT = 443 ] && \
	  tests_ran+=($elt) && \
	  ssltest_analyze_ssl $elt $site $SITE_OUTF $site_ip

	elt=curl
	CURL_ELTS="$CURL_ARGS --cacert $CAFILE --output /dev/null"
	[[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	    tests_ran+=($elt) && \
	    ssltest_curl $elt $site $SITE_OUTF $site_ip

	elt=nmap
	NMAP_ELTS="$NMAP_ARGS --host-timeout $TIMEOUT -p $SSL_PORT"
	[[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	    tests_ran+=($elt) && \
	  ssltest_nmap $elt $site $SITE_OUTF $site_ip

	elt=ssllabs
	[ $SSL_PORT = 443 ] && \
	  [[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
	  tests_ran+=($elt) && \
	  ssltest_ssllabs $elt $site $SITE_OUTF $site_ip
    done
done

# bonus
elt=alt_svc
[ $SSL_PORT = 443 ] && \
  [[ $SSLTEST_TESTS =~ .*${elt}.* ]] && \
    tests_ran+=($elt) && \
    ssltest_http2_alt_svc $elt - $SITE_OUTF -

cat $TMPDIR/${LOGP}_*.out > $OUTF
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
a=`openssl ciphers -v 'ECDH+AESGCM:ECDH+CHACHA20:ECDH+AES256:ECDH+AES128:!aNULL:!SHA1:!AESCCM' | wc -l | sed -e 's/ .*//'`
[ $? -eq 0 ] && [ "$a" -eq 0  ] && \
   WARN "no openssl ciphers" | tee -a $OUTF

DEBUG=1 DBUG "${#tests_ran[@]}" TESTS="${tests_ran[@]}"
warns=`grep -c WARN: $OUTF`
[ $? -eq 0 ] && [ "$warns" -gt 0  ] && DATE WARN "$warns warns for $site in $OUTF"
errs=`grep -c 'ERROR:\|EROR:' $OUTF`
[ $? -eq 0 ] && [ "$errs" -gt 0  ] && DATE ERROR "$errs errs for $site in $OUTF"
[ $? -eq 0 ] && [ "$warns" -eq 0 -a "$errs" -eq 0  ] && \
	DATE INFO "NO warns/errs for $site   in $OUTF"

exit $errs

#  pysslscan scan --scan=protocol.http --scan=vuln.heartbleed --scan=server.renegotiation \
#    --scan=server.preferred_ciphers --scan=server.ciphers \
#	     --report=term:rating=ssllabs.2009e --ssl2 --ssl3 --tls10 --tls11 --tls12
# /usr/local/bin/ssl-cipher-check.pl

