#!/bin/bash

# Certificate Autority Operator
# (c) 2009, Dennis Leeuw
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# A copy of the license can be obtained from
#       http://www.gnu.org/licenses/gpl-2.0.html
# or a written copy can be obtained from
#	Free Software Foundation, Inc.,
#	51 Franklin Street, Fifth Floor,
#	Boston, MA 02110-1301 USA

VERSION="0.10.2"

#----------------------------------------------------------------------#
# This script helps you to create and maintain a certificate authority #
# For the complete documentation about OpenSSL CA                      #
# see: http://pig.made-it.com/                                         #
#----------------------------------------------------------------------#

# stuff you can change:

# The directory under which everything else will be stored:
CAO_ROOT=/etc/cao
CAO_USR=root
CAO_GRP=root
CAO_WEBUSR=apache
CAO_WEBGRP=apache

#----------------- NOTHING TO BE DONE BELOW THIS LINE -----------------#

#-------------------------#
# The configuration files #
#-------------------------#

function create_opensslcnf {
	echo "# openssl.cnf file

[ ca ]
# Entry point when signing a CSR
# Used by:                              openssl ca
# cao functions:                        revoke, sign, create crl

default_ca                              = CA_default
   
[ CA_default ]
# Used by:                              openssl ca
# Referenced from:                      [ ca ]

dir					= \${ENV::CA_DOMAIN_ROOT}
serial                                  = \${ENV::CAO_SERIALDB_FILE}
database                                = \${ENV::CAO_INDEXDB_FILE}
certs					= \${ENV::CAO_SIGNED_CERTS_DIR}
new_certs_dir                           = \${ENV::CAO_CRT_DIR}
certificate                             = \${ENV::CAO_CACRT_FILE}
private_key                             = \${ENV::CAO_CAKEY_FILE}
RANDFILE				= \${ENV::CAO_RAND_FILE}
crl_dir					= \${ENV::CAO_SSL_ROOT}
crl					= \${ENV::CAO_CRL_FILE}
crl_number				= \${ENV::CAO_CRLDB_FILE}

x509_extensions				= usr_cert

default_days                            = \${ENV::DEFAULTDAYS}
default_md                              = \${ENV::DEFAULTMD}
preserve                                = no
default_crl_days			= 30

nameopt                                 = default_ca
certopt                                 = default_ca

email_in_dn                             = no
policy                                  = policy
#copy_extensions				= copy
    
[ policy ]
# Referenced from:                      [ CA_default ]
countryName                             = \${ENV::C_POLICY}
stateOrProvinceName                     = \${ENV::ST_POLICY}
localityName				= \${ENV::L_POLICY}
organizationName                        = \${ENV::O_POLICY}
organizationalUnitName                  = \${ENV::OU_POLICY}
commonName                              = \${ENV::CN_POLICY}
emailAddress                            = \${ENV::EMAIL_POLICY}
     
[ req ]
# Settings for a CSR
# And a self-signed root CRT
# Used by:                              openssl req
# cao functions:                        create_ca_cert (-x509), create_csr

default_bits                            = \${ENV::KEYBITS}
default_keyfile                         = key.pem       # name of generated keys
default_md                              = \${ENV::DEFAULTMD}
string_mask                             = nombstr       # permitted characters
							# RH uses MASK:0x2002
prompt					= no # do not ask for DN
distinguished_name                      = req_distinguished_name
attributes				= req_attributes
x509_extensions				= v3_ca
crl_extensions				= crl_ext

[ req_distinguished_name ]
# Questions to ask when creating a CSR
# Referenced from:                      [ req ]

# Settings for prompt = no
C		                        = \${ENV::DN_C_DEFAULT}
ST			                = \${ENV::DN_ST_DEFAULT}
L		                        = \${ENV::DN_L_DEFAULT}
O			                = \${ENV::DN_O_DEFAULT}
OU			                = \${ENV::DN_OU_DEFAULT}
CN		                        = \${ENV::DN_CN_DEFAULT}
emailAddress	                        = \${ENV::DN_MAIL_DEFAULT}

[ req_attributes ]
# 
# Referenced from                       [ req ]

[ usr_cert ]
# X.509 section to be used when signing a CSR
# openssl req -x509
# Referenced from                       [ CA_default ]

basicConstraints                        = \${ENV::BASICCONSTRAINTS}
subjectKeyIdentifier                    = hash
authorityKeyIdentifier                  = \${ENV::AUTHORITYKEYIDENTIFIER}

# PKIX recommendation
subjectAltName				= email:copy
issuerAltName                           = issuer:copy

nsComment                               = \${ENV::NSCOMMENT}
nsCertType                              = \${ENV::NSCERTTYPE}
keyUsage				= \${ENV::KEYUSAGE}
extendedKeyUsage			= \${ENV::EXTENDEDKEYUSAGE}

nsBaseUrl                               = \${ENV::CAO_BASE_URL}
nsCaRevocationUrl                       = \${ENV::CAO_CRL_URL}
nsCaPolicyUrl                           = \${ENV::CAO_CAPOLICY_URL}
nsRevocationUrl                         = \${ENV::CAO_REVOKE_URL}
nsRenewalUrl                            = \${ENV::CAO_RENEW_URL}
issuerAltName                           = URI:\${ENV::CAO_CACRT_URL}

[ v3_req ]
# Extensions to add to a certificate request
basicConstraints                        = \${ENV::BASICCONSTRAINTS}
keyUsage				= \${ENV::KEYUSAGE}
nsCertType                              = \${ENV::NSCERTTYPE}


[ v3_ca ]
# Extensions for a typical CA
subjectKeyIdentifier                    = hash
authorityKeyIdentifier                  = \${ENV::AUTHORITYKEYIDENTIFIER}
basicConstraints                        = \${ENV::BASICCONSTRAINTS}
keyUsage				= \${ENV::KEYUSAGE}
nsCertType                              = \${ENV::NSCERTTYPE}
subjectAltName				= email:copy
issuerAltName                           = issuer:copy

nsBaseUrl                               = \${ENV::CAO_BASE_URL}
nsCaRevocationUrl                       = \${ENV::CAO_CRL_URL}
nsCaPolicyUrl                           = \${ENV::CAO_CAPOLICY_URL}
nsRevocationUrl                         = \${ENV::CAO_REVOKE_URL}
nsRenewalUrl                            = \${ENV::CAO_RENEW_URL}
issuerAltName                           = URI:\${ENV::CAO_CACRT_URL}

[ x509_extensions ]
# Used for signing the CA root certificate
subjectKeyIdentifier                    = hash
authorityKeyIdentifier                  = \${ENV::AUTHORITYKEYIDENTIFIER}
basicConstraints                        = \${ENV::BASICCONSTRAINTS}
keyUsage				= \${ENV::KEYUSAGE}
nsCertType                              = \${ENV::NSCERTTYPE}
subjectAltName				= email:copy
issuerAltName                           = issuer:copy

nsBaseUrl                               = \${ENV::CAO_BASE_URL}
nsCaRevocationUrl                       = \${ENV::CAO_CRL_URL}
nsCaPolicyUrl                           = \${ENV::CAO_CAPOLICY_URL}
nsRevocationUrl                         = \${ENV::CAO_REVOKE_URL}
nsRenewalUrl                            = \${ENV::CAO_RENEW_URL}
issuerAltName                           = URI:\${ENV::CAO_CACRT_URL}

[ crl_ext ]
# Options when revoking a CRT

# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy 

authorityKeyIdentifier                  = keyid:always,issuer:always
" > ${CAO_SSLCNF_FILE}

	if [ $? != 0 ]; then
		echo "Could not create ${CAO_SSLCNF_FILE}!"
		exit 1
	else
		chmod 640 ${CAO_SSLCNF_FILE}
	fi
}

function create_domaincnf {
	echo "# Default DN settings for ${CAO_MY_DOMAIN}
NSCOMMENT=\"${NSCOMMENT}\"
DEFAULTDAYS=\"365\"
CADAYS=\"3650\"
KEYBITS=\"2048\"
ENCRYPTION=\"des3\"
DEFAULTMD=\"sha256\"
DN_C_DEFAULT=\"${DN_C_DEFAULT}\"
DN_ST_DEFAULT=\"${DN_ST_DEFAULT}\"
DN_L_DEFAULT=\"${DN_L_DEFAULT}\"
DN_O_DEFAULT=\"${DN_O_DEFAULT}\"
DN_OU_DEFAULT=\"${DN_OU_DEFAULT}\"
DN_CN_DEFAULT=\"${DN_CN_DEFAULT}\"
DN_MAIL_DEFAULT=\"${DN_MAIL_DEFAULT}\"
CAO_CA_FQDN=\"${CAO_CA_FQDN}\"
" > ${CA_DOMAIN_ROOT}/dn-data.cnf

	if [ $? != 0 ]; then
		echo "Could not create ${CA_DOMAIN_ROOT}/dn-data.cnf!"
		exit 1
	else
		chmod 640 ${CA_DOMAIN_ROOT}/dn-data.cnf
	fi
}

#-----------------#
# Setup Functions #
#-----------------#
function get_ca_fqdn {
	# Get FQDN address of CA server if not set
	if [ "x${CAO_CA_FQDN}" = "x" ]; then
		read -p "CA server FQDN (www.domain.com): " CAO_CA_FQDN
	fi

        # This is the base URL for all others URL addresses if not supplied
        CAO_BASE_URL="http://${CAO_CA_FQDN}/"

        # This is the link where to download the latest 
        # Certificate Revocation List (CRL)
        CAO_CRL_URL="https://${CAO_CA_FQDN}/${CAO_CRL_FILE#${CAO_SSL_ROOT}/}"

        # This is the link where the CA policy can be found
        CAO_CAPOLICY_URL="http://${CAO_CA_FQDN}/policy.html"

        # The issuer certificate
        CAO_CACRT_URL="http://${CAO_CA_FQDN}/${CAO_CACRT_FILE#${CAO_NOSSL_ROOT}/}"

        # This is the link where to revoke the certificate
        CAO_REVOKE_URL="."

        # This is the location where the certificate can be renewed
        CAO_RENEW_URL="."
}

function request_dn_data {
	# make sure N is empty
	N=""

	# Get country data
	if [ "x${DN_C_DEFAULT}" = "x" ]; then
		DN_C_DEFAULT="NL"
	fi
	until [ ${#N} = 2 ]; do
		read -p "2 letter country code [${DN_C_DEFAULT}]: " N
		if [ "x${N}" = "x" ]; then
			N=11
		fi
	done
	if [ $N != 11 ]; then
		DN_C_DEFAULT=$N
	fi

	# Get state or province data
	if [ "x${DN_ST_DEFAULT}" = "x" ]; then
		DN_ST_DEFAULT="Utrecht"
	fi
	read -p "The state or province [${DN_ST_DEFAULT}]: " N
	if [ "x$N" != "x" ]; then
		DN_ST_DEFAULT=$N
	fi

	# Get city data
	if [ "x${DN_L_DEFAULT}" = "x" ]; then
		DN_L_DEFAULT="Utrecht"
	fi
	read -p "Where your are located [${DN_L_DEFAULT}]: " N
	if [ "x$N" != "x" ]; then
		DN_L_DEFAULT=$N
	fi

	# Get organization name
	if [ "x${DN_O_DEFAULT}" = "x" ]; then
		DN_O_DEFAULT="Made IT"
	fi
	read -p "Name of the organisation [${DN_O_DEFAULT}]: " N
	if [ "x$N" != "x" ]; then
		DN_O_DEFAULT=$N
	fi

	# Get organization name
	if [ "x${DN_OU_DEFAULT}" = "x" ]; then
		DN_OU_DEFAULT="System Administration"
	fi
	read -p "Unit name within the organization [${DN_OU_DEFAULT}]: " N
	if [ "x$N" != "x" ]; then
		DN_OU_DEFAULT=$N
	fi

	# Get common name
	if [ "x${DN_CN_DEFAULT}" = "x" ]; then
		DN_CN_DEFAULT="www.example.com"
	fi
	read -p "The common name [${DN_CN_DEFAULT}]: " N
	if [ "x$N" != "x" ]; then
		DN_CN_DEFAULT=$N
	fi

	# Get organization name
	if [ "x${DN_MAIL_DEFAULT}" = "x" ]; then
		DN_MAIL_DEFAULT="admin@example.com"
	fi
	read -p "E-mail address for the common name contact [${DN_MAIL_DEFAULT}]: " N
	if [ "x$N" != "x" ]; then
		DN_MAIL_DEFAULT=$N
	fi

}

#-------------------#
# Setting Functions #
#-------------------#

# FIXME possible KEYUSAGE values: digitalSignature, nonRepudiation, keyEncipherment,
#				  dataEncipherment, keyAgreement, keyCertSign, cRLSign,
#				  encipherOnly, decipherOnly

function ca_settings {
	BASICCONSTRAINTS="CA:TRUE,pathlen:0"
	AUTHORITYKEYIDENTIFIER="keyid:always,issuer:always"
	KEYUSAGE="cRLSign,keyCertSign,digitalSignature"
	NSCERTTYPE="sslCA,emailCA,objCA"
	EXTENDEDKEYUSAGE="1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.3,1.3.6.1.5.5.7.3.4,1.3.6.1.5.5.7.3.8,1.3.6.1.5.5.7.3.9"
	C_POLICY=match
	ST_POLICY=match
	L_POLICY=match
	O_POLICY=match
	OU_POLICY=optional
	CN_POLICY=supplied
	EMAIL_POLICY=optional
}

function usr_settings {
	BASICCONSTRAINTS="CA:FALSE"
	AUTHORITYKEYIDENTIFIER="keyid,issuer:always"
	KEYUSAGE="nonRepudiation,digitalSignature,keyEncipherment"
	C_POLICY=optional
	ST_POLICY=optional
	L_POLICY=optional
	O_POLICY=optional
	OU_POLICY=optional
	CN_POLICY=supplied
	EMAIL_POLICY=optional
}

function create_dirs {
	if [ -d ${CAO_ROOT} ]; then
		echo "${CAO_ROOT} already exists"
		exit 1
	fi

	mkdir -p ${CAO_ROOT}
	chown ${CAO_USR}.${CAO_WEBGRP} ${CAO_ROOT}
	chmod 750 ${CAO_ROOT}

	# DB directories
	mkdir -p ${CAO_DB_ROOT}
	chown ${CAO_USR}.${CAO_GRP} ${CAO_ROOT}
	chmod 770 ${CAO_DB_ROOT}

	# Public directories
	mkdir -p ${CAO_PUBLIC_ROOT}
	chown ${CAO_USR}.${CAO_WEBGRP} ${CAO_PUBLIC_ROOT}
	chmod 750 ${CAO_PUBLIC_ROOT}

	mkdir -p ${CAO_NOSSL_ROOT}
	chown ${CAO_USR}.${CAO_WEBGRP} ${CAO_NOSSL_ROOT}
	chmod 770 ${CAO_NOSSL_ROOT}

	mkdir -p ${CAO_SSL_ROOT}
	chown ${CAO_USR}.${CAO_WEBGRP} ${CAO_SSL_ROOT}
	chmod 770 ${CAO_SSL_ROOT}

	mkdir -p ${CAO_CSR_DIR}
	chown ${CAO_USR}.${CAO_WEBGRP} ${CAO_CSR_DIR}
	chmod 770 ${CAO_CSR_DIR}

	mkdir -p ${CAO_CRT_DIR}
	chown ${CAO_USR}.${CAO_WEBGRP} ${CAO_CRT_DIR}
	chmod 770 ${CAO_CRT_DIR}

	# Private directories
	mkdir -p ${CAO_PRIVATE_ROOT}
	chown ${CAO_USR}.${CAO_GRP} ${CAO_PRIVATE_ROOT}
	chmod 750 ${CAO_PRIVATE_ROOT}

	mkdir ${CAO_PRIVATE_CA_DIR}
	chown ${CAO_USR}.${CAO_GRP} ${CAO_PRIVATE_CA_DIR}
	chmod 750 ${CAO_PRIVATE_CA_DIR}

	mkdir ${CAO_USRKEY_DIR}
	chown ${CAO_USR}.${CAO_GRP} ${CAO_USRKEY_DIR}
	chmod 750 ${CAO_USRKEY_DIR}

	mkdir ${CAO_SIGNED_CERTS_DIR}
	chown ${CAO_USR}.${CAO_GRP} ${CAO_SIGNED_CERTS_DIR}
	chmod 750 ${CAO_SIGNED_CERTS_DIR}
}

function create_db_files {
	if [ ! -d ${CAO_DB_ROOT} ]; then
		echo "${CAO_DB_ROOT} does not exist"
		exit 1
	fi
	echo "101010" > ${CAO_SERIALDB_FILE}
	chmod 600 ${CAO_SERIALDB_FILE}
	echo -n > ${CAO_INDEXDB_FILE}
	chmod 600 ${CAO_INDEXDB_FILE}
	echo "101010" > ${CAO_CRLDB_FILE}
	chmod 600 ${CAO_CRLDB_FILE}
	# FIXME what to do with CAO_RAND_FILE
}

function set_locations {
	# Set root dirs
	CAO_DB_ROOT=${CAO_ROOT}/${CAO_MY_DOMAIN}/db
	CAO_PRIVATE_ROOT=${CAO_ROOT}/${CAO_MY_DOMAIN}/private
	CAO_PUBLIC_ROOT=${CAO_ROOT}/${CAO_MY_DOMAIN}/public
	CAO_SSLCNF_FILE=${CAO_ROOT}/${CAO_MY_DOMAIN}/openssl.cnf
	
	# Database directory
	CAO_SERIALDB_FILE=${CAO_DB_ROOT}/serial
	CAO_INDEXDB_FILE=${CAO_DB_ROOT}/index
	CAO_CRLDB_FILE=${CAO_DB_ROOT}/crl_number
	CAO_RAND_FILE=${CAO_DB_ROOT}/random

	# Our private directory
	CAO_PRIVATE_CA_DIR=${CAO_PRIVATE_ROOT}/CA
	CAO_CAKEY_FILE=${CAO_PRIVATE_CA_DIR}/cakey.pem
	CAO_USRKEY_DIR=${CAO_PRIVATE_CA_DIR}/keys
	CAO_SIGNED_CERTS_DIR=${CAO_PRIVATE_CA_DIR}/certs

	# Our public directory
	CAO_NOSSL_ROOT=${CAO_PUBLIC_ROOT}/nossl
	CAO_CACRT_FILE=${CAO_NOSSL_ROOT}/cacert.pem
	CAO_SSL_ROOT=${CAO_PUBLIC_ROOT}/ssl
	CAO_CRL_FILE=${CAO_SSL_ROOT}/crl.pem
	CAO_CSR_DIR=${CAO_SSL_ROOT}/csr
	CAO_CRT_DIR=${CAO_SSL_ROOT}/crt
}

function export_vars {
	export AUTHORITYKEYIDENTIFIER \
	       BASICCONSTRAINTS \
	       C_POLICY \
	       CA_DOMAIN_ROOT \
	       CN_POLICY \
	       DEFAULTDAYS \
	       DEFAULTMD \
	       DN_C_DEFAULT \
               DN_CN_DEFAULT \
               DN_L_DEFAULT \
               DN_MAIL_DEFAULT \
               DN_O_DEFAULT \
               DN_OU_DEFAULT \
               DN_ST_DEFAULT \
	       EMAIL_POLICY \
	       EXTENDEDKEYUSAGE \
	       ISSUERCRT \
	       KEYBITS \
	       KEYUSAGE \
	       L_POLICY \
	       NSCERTTYPE \
	       NSCOMMENT \
	       O_POLICY \
	       OU_POLICY \
	       ST_POLICY \
	       CAO_ROOT \
	       CAO_DB_ROOT \
	       CAO_PRIVATE_ROOT \
	       CAO_PUBLIC_ROOT \
	       CAO_SERIALDB_FILE \
	       CAO_INDEXDB_FILE \
	       CAO_CRLDB_FILE \
	       CAO_RAND_FILE \
	       CAO_PRIVATE_CA_DIR \
	       CAO_CAKEY_FILE \
	       CAO_USRKEY_DIR \
	       CAO_SIGNED_CERTS_DIR \
	       CAO_NOSSL_ROOT \
	       CAO_CACRT_FILE \
	       CAO_SSL_ROOT \
	       CAO_CRL_FILE \
	       CAO_CSR_DIR \
	       CAO_CRT_DIR \
	       CAO_BASE_URL \
	       CAO_CRL_URL \
	       CAO_CAPOLICY_URL \
	       CAO_REVOKE_URL \
	       CAO_RENEW_URL \
	       CAO_CACRT_URL
}

#---------------#
# Key functions #
#---------------#
function create_param {
	export_vars
	# Create parameters for dsa, dh

        openssl ${KEY}param ${KEYBITS} -out ${PRM_FILE}
	if [ $? != 0 ]; then
		echo "Could not create parameters file for ${KEY}"
		exit 1
	fi

        chmod 600 ${PRM_FILE}
}

function create_key {
	export_vars
        if [ "x${KEY}" = "xdsa" ]; then
                OPTIONS="${DSA_PARAM_FILE}"
        elif [ "x${KEY}" = "xrsa" ]; then
                OPTIONS="${KEYBITS}"
        fi

	echo "Creating a ${KEY} key"
        openssl gen${KEY} -${ENCRYPTION} -out ${KEY_FILE} ${OPTIONS} 2>/dev/null
	if [ $? != 0 ]; then
		echo "Could not create the ${KEY} key file"
		exit 1
	fi

        chmod 600 ${KEY_FILE}
}

#-----------------------#
# Certificate functions #
#-----------------------#

function get_cert_type {
	# CA objects
	# EXTENDEDKEYUSAGE="1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2,1.3.6.1.5.5.7.3.3,1.3.6.1.5.5.7.3.4,1.3.6.1.5.5.7.3.8,1.3.6.1.5.5.7.3.9"

	CA=N
	KEYUSAGE="nonRepudiation,digitalSignature,keyEncipherment"
	read -p "Is this a CA certificate? [N] " N
	if [ "$N" = "Y" ] || [ "$N" = "y" ]; then
		CA=Y
		KEYUSAGE="cRLSign,keyCertSign"
	fi

	read -p "Certificate type <server,client,email,code,timestamp,ocspsign>: " N
	local OLD_IFS="${IFS}"
	local IFS='\,'
	for ELM in $N; do
		if [ ${ELM} = "server" ]; then
			NSTYPE="server"
			EKU="1.3.6.1.5.5.7.3.1"
			if [ "$CA" = "Y" ]; then
				NSTYPE="sslCA"
				EKU=""
			fi
		elif [ ${ELM} = "client" ]; then
			NSTYPE="client"
			EKU="1.3.6.1.5.5.7.3.2"
		elif [ ${ELM} = "code" ]; then
			NSTYPE="objsign"
			EKU="1.3.6.1.5.5.7.3.3"
			if [ "$CA" = "Y" ]; then
				NSTYPE="objCA"
				EKU=""
			fi
		elif [ ${ELM} = "email" ]; then
			NSTYPE="email"
			EKU="1.3.6.1.5.5.7.3.4"
			if [ "$CA" = "Y" ]; then
				NSTYPE="emailCA"
				EKU=""
			fi
		elif [ ${ELM} = "timestamp" ]; then
			NSTYPE=""
			EKU="1.3.6.1.5.5.7.3.8"
		elif [ ${ELM} = "ocspsign" ]; then
			NSTYPE=""
			EKU="1.3.6.1.5.5.7.3.9"
		fi

		# Create NSCERTTYPE
		if [ "x${NSTYPE}" != "x" ]; then
			if [ "x${NSCERTTYPE}" = "x" ]; then
				NSCERTTYPE="${NSTYPE}"
			else
				NSCERTTYPE="${NSCERTTYPE},${NSTYPE}"
			fi
		fi

		# Create EXTENDEDKEYUSAGE
		if [ "x${EKU}" != "x" ]; then
			if [ "x${EXTENDEDKEYUSAGE}" = "x" ]; then
				EXTENDEDKEYUSAGE="${EKU}"
			else
				EXTENDEDKEYUSAGE="${EXTENDEDKEYUSAGE},${EKU}"
			fi
		fi

	done
	IFS="${OLD_IFS}"
}

function create_csr {
	request_dn_data

	get_cert_type

	# Set user stuff
	usr_settings
	get_ca_fqdn

	export_vars

	create_key

	echo "Creating CSR... "
	openssl req -new -config ${CAO_SSLCNF_FILE} \
		    -reqexts v3_req \
	            -key ${KEY_FILE} -out ${CSR_FILE}
	if [ $? != 0 ]; then
		echo "Could not create ${CSR_FILE}"
		exit 1
	fi

}

function create_pkcs12 {
	openssl pkcs12 -chain -export -in /etc/cao/CA/private/CA/certs/dbg-win.op.umcutrecht.nl.pem -inkey /etc/cao/CA/private/CA/keys/dbg-win.op.umcutrecht.nl.key -out file.p12 -CSP "DBG-WIN Certificate"
}


#----------------#
# Main Functions #
#----------------#
function create_ca_cert {
	# Syntax
	# create_ca_cert type

	KEY=$1
	if [ "x${KEY}" = "xdsa" ]; then
		PARM_FILE=${PARM_DIR}/dsaparam.pem
		create_dsa_param
	fi

	KEY_FILE=${CAO_CAKEY_FILE}
	CERT_FILE=${CAO_CACRT_FILE}

	DEFAULTDAYS=${CADAYS}
	
	# Create the CA certificate
	# create_key

	echo "Signing CA certificate"
	get_ca_fqdn
	export_vars

	# Create CA CSR
	openssl req -config ${CAO_SSLCNF_FILE} -new -newkey ${KEY}:${KEYBITS} -out ${CAO_CACRT_FILE%.pem}.csr -keyout ${KEY_FILE}

	# Create a selfsigned certificate
	openssl x509 -trustout -extfile ${CAO_SSLCNF_FILE} \
			  -extensions v3_ca \
			  -days ${CADAYS} -signkey ${KEY_FILE} \
			  -trustout -req -in ${CAO_CACRT_FILE%.pem}.csr \
			  -out ${CAO_CACRT_FILE} -outform PEM
	if [ $? != 0 ]; then
		echo "Could not create ${CAO_CACRT_FILE}"
		exit 1
	fi

	# Create DER version
	openssl x509 -in ${CAO_CACRT_FILE} -outform DER \
		-out ${CAO_CACRT_FILE%.pem}.der
	
	# Create HASH version
	CAO_CA_HASH=`openssl x509 -noout -hash -in ${CAO_CACRT_FILE}`
	cp ${CAO_CACRT_FILE} ${CAO_CACRT_FILE%/*}/${CAO_CA_HASH}.0
}

function check_certs_check {
	# Exit codes
	# 0 valid
	# 1 error
	# 253 expires within a month
	# 254 invalid

	CURR_DATE[0]=`date +%Y`
	CURR_DATE[1]=`date +%b`

	# Get the Not After field
	if [ -f $1 ]; then
		CRT_LINE=`openssl x509 -noout -enddate -in $1`
		CRT_ARR=(`echo ${CRT_LINE#*=}`)
		# 0 month
		# 1 day
		# 2 time
		# 3 year
		# 4 zone
	else
		echo "Cert: $1 does not exits"
		exit 1
	fi

	# Compare year less then
	if [ ${CRT_ARR[3]} -lt ${CURR_DATE[0]} ]; then
		return 254
	fi

	# Compare year greater then
	if [ ${CRT_ARR[3]} -gt ${CURR_DATE[0]} ]; then
		return 0
	fi

	# Next test is only when the years are equal
	months=(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
	for ((I=0; $I <= ${#months[*]}; I=$(($I+1)) )); do
		if [ "${CRT_ARR[0]}" = "${months[$I]}" ]; then
			M_CRT_N=$I
			break
		fi
	done

	for ((I=0; $I <= ${#months[*]}; I=$(($I+1)) )); do
		if [ "${CURR_DATE[1]}" = "${months[$I]}" ]; then
			M_NOW_N=$I
			break
		fi
	done

	if [ ${M_CRT_N} -lt ${M_NOW_N} ]; then
		return 254
	fi

	if [ $((${M_CRT_N}-1)) -lt ${M_NOW_N} ]; then
		echo "$CERT expires in a month"
		return 253
	fi
	echo return
	return 0
}

function check_certs {
	# First check CA certificate
	check_certs_check ${CA_DOMAIN_ROOT}/cacert.pem

	local I=0
	if [ $? = 253 ]; then
		MONTH_ARRAY=[$I]="cacert.pem expires within a month"
		I=$(($I+1))
	elif [ $? = 254 ]; then
		MONTH_ARRAY=[$I]="cacert.pem is invalid"
		I=$(($I+1))
	fi

	# Check other Certificates
	for CERT in `ls ${CA_DOMAIN_ROOT}/newcerts/*`; do
		check_certs_check ${CERT}
		if [ $? = 253 ]; then
			MONTH_ARRAY[$I]="${CERT} expires within a month"
			I=$(($I+1))
		elif [ $? = 254 ]; then
			MONTH_ARRAY=[$I]="${CERT} is invalid"
			I=$(($I+1))
		fi
	done

	# Output our findings
	if [ ${#MONTH_ARRAY[*]} != 0 ]; then
		echo ${MONTH_ARRAY[*]}
	fi

	exit 0
}

#----------------#
# Help Functions #
#----------------#
function print_version {
	COMM=${0##*/}
	echo "$COMM version: ${VERSION}"
}

function print_license {
	cat <<EOF
Certificate Autority Operator
(c) 2009, Dennis Leeuw

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

A copy of the license can be obtained from
	http://www.gnu.org/licenses/gpl-2.0.html
or a written copy can be obtained from
	Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor,
	Boston, MA 02110-1301 USA.
EOF
}

function print_help {
	print_version
	echo "The default domain is CA, which should be the root CA."
	echo "If in the following commands the <domain> part is omitted the root CA"
	echo "is assumed. Sub authorities can be created by filling in the domain."
	echo "$COMM <domain> [init|remove]"
	echo "	init   - initializes a domain"
	echo "	remove - removes a domain"
	echo
	echo "$COMM <domain> create [cacert] [rsa|dsa]"
	echo
	echo "$COMM <domain> create [key|csr] [rsa|dsa] <object name>"
	echo "	[key|csr] create a key or a CSR, csr will create a new key"
	echo "	[rsa|dsa] type of key to be created"
	echo "	<object name> the name for the object, a FQDN for systems or"
	echo "	              an e-mail address for an e-mail certificate"
	echo
	echo "$COMM <domain> sign <object name>"
	echo
	echo "$COMM <domain> revoke <serial> <reason>"
	echo "	Put the certificate with <serial> on the CRL list."
	echo "	<reason> can be on of: keyCompromise, CACompromise,"
	echo "	         affiliationChanged, superseded, cessationOfOperation, "
	echo "	         removeFromCRL, privilegeWithdrawn, aACompromise"
	echo "	         If none is given unspecified is used."
	echo
	echo "$COMM <domain> hold <serial> <action>"
	echo "	Puts a certificate on hold and gives an action to be taken"
	echo "	<action> can be: holdInstructionCallIssuer or"
	echo "	                 holdInstructionReject (which is the default)"
	echo
	echo "$COMM <domain> check certs"
	echo
	echo "$COMM <domain> create crl"
	echo
	echo "$COMM [version|license|help]"
	echo "	Outputs the $COMM version, license or help and exits"
}

#----------------#
# Main functions #
#----------------#
function main_init {
	set_locations
	create_dirs
	create_db_files
	export_vars

	# if openssl.cnf does not exist create it
	if [ ! -f ${CAO_SSLCNF_FILE} ]; then
		create_opensslcnf
	fi

	# Get the CA domain data
	get_ca_fqdn

	request_dn_data

	# Output data
	NSCOMMENT="${DN_O_DEFAULT} Generated Certificate"


	create_domaincnf
	echo "Check the settings in:"
	echo "	${CA_DOMAIN_ROOT}/dn-data.cnf"
}

function main_remove {
	rm -rf ${CA_DOMAIN_ROOT}
}

function main_create {
	set_locations
	case $1 in
	   cacert)
		ca_settings
		create_ca_cert $2
	   ;;
	   crl)
	   	ca_settings
		get_ca_fqdn
		export_vars
	   	openssl ca -config ${CAO_SSLCNF_FILE} \
			-gencrl -out ${CAO_CRL_FILE} -crlexts crl_ext
	   ;;
	   key|csr)
		# Get the key type to be used
	       	if [ "x$2" != "x" ]; then
			KEY=$2
		else
			print_help
			exit 1
		fi

		# Set the files to be used
		if [ "x$3" != "x" ]; then
			KEY_FILE=${CAO_USRKEY_DIR}/${3}.key
			CSR_FILE=${CAO_CSR_DIR}/${3}.csr
			DN_CN_DEFAULT=$3
		else
			print_help
			exit 1
		fi
		create_$1
	   ;;
	   *)
		print_help
		exit 1
	   ;;
	esac
}

function main_sign {
	set_locations
	if [ "x$1" != "x" ]; then
		CSR_FILE=${CAO_CSR_DIR}/${1}.csr
		CRT_FILE=${CAO_SIGNED_CERTS_DIR}/${1}.pem
	else
		print_help
		exit 1
	fi

	usr_settings
	get_ca_fqdn
	get_cert_type

	export_vars
	echo "Signing the CSR file... "
	openssl ca -config ${CAO_SSLCNF_FILE} -days ${DEFAULTDAYS} \
	           -in ${CSR_FILE} -out ${CRT_FILE} -batch
	if [ $? != 0 ]; then
		echo "Could not sign ${CSR_FILE}"
		exit 1
	#else
		# Clean up CSR stuff
		#rm -f ${CSR_FILE}
	fi
}

function main_check {
	case $1 in
		certs)
			check_certs
		;;
		*)
			print_help
			exit 1
		;;
	esac
}

function main_revoke {
	NSCERTTYPE="."
	EXTENDEDKEYUSAGE="."
	case $2 in
		keyCompromise|CACompromise|affiliationChanged|superseded|cessationOfOperation|removeFromCRL|privilegeWithdrawn|aACompromise)
			REVOKE_REASON=$2
		;;
		*)
			REVOKE_REASON="unspecified"
		;;
	esac

	# FIXME: we should do something with -crl_CA_compromise time and -crl_compromise time

	set_locations
	ca_settings
	get_ca_fqdn
	export_vars
	if [ -f ${CAO_CRT_DIR}/${1}.pem ]; then
		openssl ca -config ${CAO_SSLCNF_FILE} \
		           -revoke ${CAO_CRT_DIR}/${1}.pem \
			   -crl_reason ${REVOKE_REASON}
	else
		echo "Certificate for $1 does not exits"
		exit 1
	fi
}

function main_hold {
	# Indicates the action to be taken after encountering a certificate
	# that has been placed on hold.

	NSCERTTYPE="."
	EXTENDEDKEYUSAGE="."
	case $2 in
		holdInstructionCallIssuer|holdInstructionReject)
			HOLD_REASON=$2
		;;
		*)
			# RFC3280 says that holdInstructionNone is
			# depricated so the safe bet is to act as
			# if the certificate is revoked
			HOLD_REASON="holdInstructionReject"
		;;
	esac

	set_locations
	ca_settings
	get_ca_fqdn
	export_vars
	if [ -f ${CAO_CRT_DIR}/${1}.pem ]; then
		openssl ca -config ${CAO_SSLCNF_FILE} \
		           -revoke ${CAO_CRT_DIR}/${1}.pem \
			   -crl_reason certificateHold \
			   -crl_hold ${HOLD_REASON}
	else
		echo "Certificate for $1 does not exits"
		exit 1
	fi
}

function main_renew {
	# FIXME
        # See x509 man page
        openssl x509 -x509toreq -signkey private.key -out newcsr.csr -in oldcert.pem
        # resign csr
}

function sub_main {
	case $1 in
		init|remove|create|sign|revoke|check|renew)
			# check if we have been in this function before
			if [ "x${CAO_MY_DOMAIN}" = "x" ]; then
				CAO_MY_DOMAIN=CA
			fi

			# Set directory and load settings if possible
			CA_DOMAIN_ROOT=${CAO_ROOT}/${CAO_MY_DOMAIN}
			if [ -d ${CA_DOMAIN_ROOT} ]; then
				. ${CA_DOMAIN_ROOT}/dn-data.cnf
			fi
			export_vars


			# Call the right function
			local function=$1
			shift
			main_$function $@
			exit 0
		;;
		version)
			print_version
			exit 0
		;;
		help)
			print_help
			exit 0
		;;
		license)
			print_license
			exit 0
		;;
		*)
			if [ "x$1" = "x" ]; then
				print_help
				exit 0
			else
				# FIXME
				echo "Sub domain function is not implemented yet!"
				exit 0
				CAO_MY_DOMAIN=$1
				shift
				sub_main $@
			fi
		;;
	esac

}

#------#
# Main #
#------#
# FIXME dh en dsa key generation

sub_main $@
exit 0

# END #
