#!/bin/sh # # ssl_mgmt, Copyright © 2012 Thomas Preud'homme # # 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 3 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. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # email_account is a helper to create e-mail accounts in a configuration # using Postfix mail transport agent with vhosts and Cyrus IMAP server, # in a Debian environment. set -u # Display usage. usage () { local - progname progname=$1 echo "Usage :" echo echo "$progname [-c | -g] renew { | }" echo "$progname -h" echo echo "First form renew the certificate specified as a file or a service name" echo echo "Possible option:" echo echo "-c Only generate the configuration" echo "-g Stop after generating the certificate and keys: do not overwrite" echo " existing ones" echo echo "Second form prints this help." } # @param question Question to ask # @return 0 if answer is positive, 1 else # # Ask question to the user with a default choice set to no. ask_user_default_no () { local - answer answer="unset" echo -n "$1 [y/N] " while [ -n "$answer" -a "$answer" != "y" -a "$answer" != "Y" -a "$answer" != "n" -a "$answer" != "N" ] do read answer if [ -z "$answer" -o "$answer" = "n" -o "$answer" = "N" ] then return 1 fi if [ "$answer" = "y" -o "$answer" = "Y" ] then break fi echo -n "$2 [y/N] " done return 0 } # Parse arguments and test their number and value is correct. parse_args () { local - user domain action config_only="" no_overwrite="" while getopts "cgh" opt do case $opt in "c") config_only=yes ;; "g") no_overwrite=yes ;; "h") if [ $# -gt 1 ] then echo "Error! Too many arguments." >&2 exit 1 fi usage $(basename "$0") exit 0 ;; esac done eval action="\${$OPTIND:-}" if [ $(($#-$OPTIND+1)) -ne 2 -o "$action" != "renew" ] then usage $(basename "$0") exit 0 fi eval service="\$$((OPTIND+1))" } # @param file the file we wish to access # @param mode the mode we wish to access the file in. # It must be either "READ" or "WRITE". # # Exit if we are unable to access the given file with requested access mode # NB: this function does not return. exit_if_no_access () { accessedFile="$1" accessMode="$2" case $accessMode in "READ") [ -r $accessedFile ];; "WRITE") [ -w $accessedFile ];; esac if [ ! $? -eq 0 ] then echo "You do not have enough rights to access ${accessedFile}." echo "Permission of $accessedFile are:" getfacl "$accessedFile" fi } # Set all variables configuring the overall behavior of ssl_mgmt. A default # value is provided and overriden if set in the configuration file set_variables () { cnfFilePath=${cnfFilePath:-/etc/${0##*/}.conf} exit_if_no_access "$cnfFilePath" "READ" . $cnfFilePath workDir=${workDir:-/usr/lib/ssl/CA} csrSubdir=${csrSubdir:-csr} certSubdir=${certSubdir:-newcerts} keySubdir=${keySubdir:-newkeys} certDestDir=${certDestDir:-/etc/ssl/certs} keyDestDir=${keyDestDir:-/etc/ssl/private} CACertPath=${CACertPath:-$certDestDir/ca-cert.pem} CAKeyPath=${CAKeyPath:-$keyDestDir/ca-key.pem} opensslCnfFile=openssl.cnf rootCAPwdPath=${rootCAPwdPath:-/root/passwords/root_ca} managedCerts=${managedCerts:-$(xargs $opensslCnfTmpFile if ask_user_default_no "Do you want to edit the openssl configuration file?" then if [ -n "${EDITOR:-}" ] then $EDITOR $opensslCnfTmpFile else editor $opensslCnfTmpFile fi fi mv $opensslCnfTmpFile $opensslCnfFile } # @param service the name of the service associated with the certificate to # renew # @param certPath the absolute path to the certificate to renew # @param keyPath the absolute path to the key associated with the certificate # to renew # # Generate the certificate, key and combine key+certificate based on the values # of the existing certificate generate_cert () { local - service certPath keyPath reqFile certFile keyFile keycertFile service="$1" certPath="$2" keyPath="$3" reqFile=${service}-req.pem certFile=${certPath##*/} keyFile=${keyPath##*/} keycertFile=${service}-keycert.pem openssl req -new -nodes -out $csrSubdir/$reqFile -keyout $keySubdir/$keyFile -config $opensslCnfFile openssl req -in $csrSubdir/$reqFile -text -verify -noout if ! ask_user_default_no "Is the Certificate Signing Request correct?" then return 1 fi getfacl "$keyPath" | setfacl --set-file=- $keySubdir/$keyFile chown --reference="$keyPath" $keySubdir/$keyFile if [ -z "$no_overwrite" ] then if [ ! -f "$keyDestDir/$keyFile" ] then echo "Error! No file named $keyFile in directory $keyDestDir:" >&2 echo "there might be a problem." >&2 fi mv $keySubdir/$keyFile $keyDestDir fi openssl ca -batch -config $opensslCnfFile -cert $CACertPath \ -keyfile $CAKeyPath -passin file:$rootCAPwdPath \ -out $certSubdir/$certFile -infiles $csrSubdir/$reqFile getfacl "$certPath" | setfacl --set-file=- $certSubdir/$certFile chown --reference="$certPath" $certSubdir/$certFile if [ -z "$no_overwrite" ] then if [ ! -f "$certDestDir/$certFile" ] then echo "No file named $certFile in directory $certDestDir:" >&2 echo "there might be a problem" >&2 fi fingerprint="$(openssl x509 -in "$certPath" -noout -fingerprint)" fingerprint=${fingerprint#*=} if [ -n "$notifiedUsers" ] then eval notifySubject="\"$notifySubject\"" eval notifyTemplate="\"$notifyTemplate\"" mail -s "$notifySubject" $notifiedUsers < $keyDestDir/$keycertFile getfacl "$keyPath" | setfacl --set-file=- $keyDestDir/$keycertFile chown --reference="$keyPath" $keyDestDir/$keycertFile return 0 } main () { local - ret servicesok certPath keyPath ret=0 parse_args "$@" set_variables cd $workDir if [ "${service}" = "all" ] then exit_if_no_access "$managedCerts" "READ" services="" for service in $managedCerts do services="$services $service" done else services=${service} fi exit_if_no_access "$certDestDir" "WRITE" exit_if_no_access "$keyDestDir" "WRITE" for service in $services do servicesok="" certPath="$service" if [ -f "$certPath" ] then service="${service##*/}" service="${service%.*}" keyPath="$keyDestDir/${service}.key" else certPath="$certDestDir/${service}-cert.pem" keyPath="$keyDestDir/${service}-key.pem" fi if [ ! -f "$certPath" ] then ret=1 continue fi exit_if_no_access "$certPath" "READ" exit_if_no_access "$keyPath" "READ" exit_if_no_access "$rootCAPwdPath" "READ" get_cert_params "$certPath" generate_config if [ -n "$config_only" ] then continue fi if ! generate_cert "$service" "$certPath" "$keyPath" then ret=1 else servicesok="$servicesok${servicesok:+ }$service" fi done if [ -z "$config_only" ] then if [ -n "$servicesok" ] then echo "You should restart the following services: $servicesok" else echo "No certificate generated" fi fi return $ret } main "${@:-""}" exit $?