#!/bin/bash

# Copyright: 2023 Virtuozzo International GmbH

# Licensed 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.
VERSION="1";
$PROGRAM 'sed';
$PROGRAM 'grep';

JAVA_HOME=$(readlink -f /usr/bin/java | $SED "s:bin/java::");
DESCRIPTION="Manipulate keystore certificates";
DEFAULT_ACTION="Usage";
SSL_CONFIG_DIR="/var/lib/jelastic/SSL";
$PROGRAM 'wget';

DEFAULT_DOMAIN="jelastic";
$PROGRAM 'openssl';
_OPENSSL=$OPENSSL;
_KEYTOOL="${JAVA_HOME}/bin/keytool";
crtUploaded='cert';
crtcaUploaded='intermediate';
keyUploaded='key';

include output;

function getKeyPass() {
    echo "changeit";
}

function getStoragePass() {
    echo "changeit";
}

function doUsage() {
    showUsageMessage;
}

function checkSSLDirectory(){
    [ ! -d "$SSL_CONFIG_DIR" ] && mkdir -p "$SSL_CONFIG_DIR";
}

function getCommonNameFromCertificate(){
    local common_name=$($OPENSSL x509 -noout -in ${SSL_CONFIG_DIR}/${DEFAULT_DOMAIN}.crt  -subject 2>>$ACTIONS_LOG | $GREP -i "subject.*CN=.*" | $SED -n "s/.*CN=\(.*\)\/.*/\1/p");
    [ ! -z "$common_name" ] && echo "$common_name" || exit 4;
}

function getDNSAltNamesFromCertificate(){
    local dns_alt_names=$($OPENSSL  x509 -text -noout -in ${SSL_CONFIG_DIR}/${DEFAULT_DOMAIN}.crt -subject 2>>$ACTIONS_LOG | $GREP -A 1 "Subject Alternative Name" | tail -n 1 |  $SED "s/[.\ ]\{0,\}DNS://g");
    [ ! -z "$dns_alt_names" ] && echo "$dns_alt_names" || exit 4;
}

function getExpireDateFromCertificate(){
    local expiredate_raw=$($OPENSSL  x509 -text -noout -in ${SSL_CONFIG_DIR}/${DEFAULT_DOMAIN}.crt  -subject -enddate 2>>$ACTIONS_LOG | $GREP notAfter | $SED 's/^notAfter=//');
    local expiredate=$(date --utc --date="$expiredate_raw" "+%Y-%m-%d %H:%M:%S");
    [ ! -z "$expiredate" ] && echo "$expiredate" || exit 4;
}

# return: 
# 1 - search string not found in provided file
# 2 - empty file
function validateCert(){
    local crtFL=$1
    local searchString=$2
    local exitCode=0

    if [ -s "$crtFL" ]; then
	$GREP -qi ${searchString} ${crtFL} || exitCode=1
    else
	exitCode=2
fi
    return $exitCode
}

function checkIfKeyEncrypted(){
    local crtFL=$1
    local searchString=$2
    $GREP -qi ${searchString} ${crtFL} && return 1
    openssl pkey -in ${crtFL} -passin pass:$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 8) -noout 2>&1
    return $?
}

function doDownloadKeys(){
    local domain="$DEFAULT_DOMAIN";
    if [ -f "${SSL_CONFIG_DIR}/${crtUploaded}" -a -f "${SSL_CONFIG_DIR}/${crtcaUploaded}" -a -f "${SSL_CONFIG_DIR}/${keyUploaded}" ]; then
	mv -f ${SSL_CONFIG_DIR}/${crtUploaded} $SSL_CONFIG_DIR/${domain}.crt;
	mv -f ${SSL_CONFIG_DIR}/${crtcaUploaded} $SSL_CONFIG_DIR/${domain}-ca.crt;
	mv -f ${SSL_CONFIG_DIR}/${keyUploaded} $SSL_CONFIG_DIR/${domain}.key;
    elif [ -f "${SSL_CONFIG_DIR}/customssl.conf" ]; then
	cp -f "${SSL_CONFIG_DIR}/customssl.conf" "${SSL_CONFIG_DIR}/${domain}.conf";
	source "$SSL_CONFIG_DIR/${domain}.conf";
	$WGET --no-check-certificate "$cert" --output-document="$SSL_CONFIG_DIR/${domain}.crt" > /dev/null 2>&1
	$WGET --no-check-certificate "$intermediate" --output-document="$SSL_CONFIG_DIR/${domain}-ca.crt" > /dev/null 2>&1
	$WGET --no-check-certificate "$key" --output-document="$SSL_CONFIG_DIR/${domain}.key" > /dev/null 2>&1
    else
	writeJSONResponseErr "result=>4147" "message=>Certificates not uploaded"; echo 147 >${resultFile:-/tmp/sslerror}; die -q; exit 147;
    fi

    cert="$SSL_CONFIG_DIR/${domain}.crt";
    intermediate="$SSL_CONFIG_DIR/${domain}-ca.crt";
    key="$SSL_CONFIG_DIR/${domain}.key";

    validateCert "$SSL_CONFIG_DIR/${domain}.crt" "CERTIFICATE"
    local res=$?
    case $res in
	1 ) writeJSONResponseErr "result=>4011" "message=>Uploaded file isn't a certificate!"; echo 211 >${resultFile:-/tmp/sslerror}; die -q; exit 211;
	;;
	2 ) writeJSONResponseErr "result=>4012" "message=>Could not receive certificate!"; echo 212 >${resultFile:-/tmp/sslerror}; die -q; exit 212;
	;;
    esac

    validateCert "$SSL_CONFIG_DIR/${domain}-ca.crt" "CERTIFICATE"
    res=$?
    case $res in
	1 ) writeJSONResponseErr "result=>4011" "message=>Uploaded file isn't a certificate!"; echo 211 >${resultFile:-/tmp/sslerror}; die -q; exit 211;
	;;
	2 ) writeJSONResponseErr "result=>4013" "message=>Could not receive CA certificate"; echo 213 >${resultFile:-/tmp/sslerror}; die -q; exit 213;
	;;
    esac

    validateCert "$SSL_CONFIG_DIR/${domain}.key" "KEY"
    res=$?
    case $res in
	1 ) writeJSONResponseErr "result=>4015" "message=>Uploaded file isn't a private key!"; echo 215 >${resultFile:-/tmp/sslerror}; die -q; exit 215;
	;;
	2 ) writeJSONResponseErr "result=>4016" "message=>Could not receive private key!"; echo 216 >${resultFile:-/tmp/sslerror}; die -q; exit 216; 
	;;
    esac

    checkIfKeyEncrypted "$SSL_CONFIG_DIR/${domain}.key" "ENCRYPTED"
    res=$?
    [ "$res" -ne "0" ] && { writeJSONResponseErr "result=>4180" "message=>Uploaded key is encrypted!"; echo 217 >${resultFile:-/tmp/sslerror}; die -q; exit 217; }

    local cert_mod=$(${OPENSSL} x509 -noout -modulus -in ${cert} 2>>$ACTIONS_LOG)
    local key_mod=$(${OPENSSL} rsa -noout -modulus -in ${key} 2>>$ACTIONS_LOG)
    [[ "${cert_mod}" != "${key_mod}" ]] && {
	writeJSONResponseErr "result=>4014" "message=>Certificate does not match key!";
	echo 214 >${resultFile:-/tmp/sslerror}; die -q; exit 214;
    }
    return 0;
}

function addSSLToKeystore() {
    [ $# -ne 1 ] && doUsage;
    local domain="$1";
    local tmpfile=$(mktemp);
    local keystore="$SSL_CONFIG_DIR/${domain}.jks"

    #make pkcs12
    $OPENSSL pkcs12 -export -in "$cert" -inkey "$key" -out "$tmpfile" -name "$domain" -caname root -password pass:$(getKeyPass) -certfile "$intermediate" || {
        return 1;
    }
    ${_KEYTOOL} -importKEYSTORE -noprompt -deststorepass $(getStoragePass) -destkeypass $(getKeyPass) -destKEYSTORE "$keystore" -srcKEYSTORE "$tmpfile" -srcstoretype PKCS12 -srcstorepass $(getStoragePass) -alias "$domain"  > /dev/null 2>&1 || {
        return 1;
    }
    echo y | ${_KEYTOOL} -import -trustcacerts -alias intermed -file ${intermediate} -KEYSTORE ${keystore} -keypass $(getKeyPass) -storepass $(getStoragePass) > /dev/null 2>&1;
    return $?;
}

function removeSSL(){
    [ $# -ne 1 ] && doUsage;
    local domain="$1";
    [ -d "$SSL_CONFIG_DIR" ] && {
        rm -Rf $SSL_CONFIG_DIR/*.crt;
        rm -Rf $SSL_CONFIG_DIR/*.key;
        rm -Rf $SSL_CONFIG_DIR/*.jks;
    }
}

function doCheck() {
    local  domain=${1} cert_DOMAIN cert_mod key_mod
    cert_DOMAIN=$(${OPENSSL} x509 -noout -text -in ${cert} 2>>$ACTIONS_LOG | $GREP "Subject.*CN=.*" | $SED 's/.*CN=\(.*\)/\1/');
    cert_mod=$(${OPENSSL} x509 -noout -modulus -in ${cert} 2>>$ACTIONS_LOG)
    key_mod=$(${OPENSSL} rsa -noout -modulus -in ${key} 2>>$ACTIONS_LOG)
    [[ "${domain}" != "${cert_DOMAIN}" ]] && {
	#runIfExists log "invalid domain";
        return 1;
    }
    [[ "${cert_mod}" != "${key_mod}" ]] && {
	#runIfExists log "key did not matching cert";
	return 1;
    }
    return 0;
}

function doInstall(){
    removeSSL "$DEFAULT_DOMAIN";
    doDownloadKeys "$DEFAULT_DOMAIN";
    local domain=$(getCommonNameFromCertificate);
    checkSSLDirectory;
	addSSLToKeystore "$DEFAULT_DOMAIN" ;
}

function doRemove(){
    removeSSL "$DEFAULT_DOMAIN" ;
}

function doCheckDomain(){
    [ -f "${SSL_CONFIG_DIR}/${DEFAULT_DOMAIN}.crt" ] || { writeJSONResponseErr "result=>4010" "message=>No certificates installed"; return 1; };
    local domain=$(getCommonNameFromCertificate);
    local dns_alt_names=$(getDNSAltNamesFromCertificate 2>/dev/null);
    local expiredate=$(getExpireDateFromCertificate);
    [ ! -z "$dns_alt_names" ] && [  ! -z "$expiredate" ] && { writeJSONResponseOut "result=>0" "domain=>${dns_alt_names}" "expiredate=>${expiredate}"; } || { writeJSONResponseErr "result=>4009" "message=>Could not detect valid domain for uploaded certificate"; return 1; };
}

function describeCheck() {
    echo "Check certificate";
}

function describeCheckDomain() {
    echo "Check domain name for uploaded certificate";
}

function describeDownloadKeys() {
    echo "";
}

function describeInstall() {
    echo "Install ssl keys";
}

function describeRemove() {
    echo "Remove ssl certificate";
}
