#!/bin/sh # # /usr/local/sbin/localmksslcerts # Copyright 2001-2004 Jonas Smedegaard # # $Id: localmksslcerts,v 1.20 2005-10-18 12:32:02 jonas Exp $ # # Generate certificates for mail (and other) servers # Based on uw-imapd-ssl post-install script # # TODO: Use getopts # TODO: Add symlink from CA certificate to cacert.pem if non-existing # TODO: Default CA certificate is cacert.pem if --cacert not set set -e prg=$(basename $0) copyright="(C) 2001-2004 Jonas Smedegaard " # Set some defaults PATH="$PATH:/usr/bin/ssl" DAYS2EXPIRE="365" SSLCERTDIR="/etc/ssl/certs" SSLKEYDIR="/etc/ssl/private" usage() { echo "$prg, $copyright Usage: $prg [--fqdn ] [...] --daemon [...] [--force] or: $prg [...] [-f] Options: --fqdn Fully Qualified Domain Name for this host. --cn Country Name (2 letter code) --state State or Province Name (full name) --loc Locality Name (eg, city) --org Organisation/company --ou Organisational unit/department --daemon Daemon(s) in need for a certificate (certificate is generated for each daemon) --issuer Email address of entity issuing certificate --cert Use certified host certificate --cacert Where to store host certificate if missing --makeca Create CA certificate if missing -f, --force Force overwriting existing certificate(s) -h, --help This help text Examples: Create certs for UW imapd/popd, bound to default host cert if there: localmksslcerts imapd pop3d Create host cert for non-default hostname, overwriting stray files: localmksslcerts --fqdn mail.jones.dk --cert --force Create host cert for non-default hostname and bind to UW daemons: localmksslcerts --fqdn mail.jones.dk --cert --force imapd pop3d Create CA-signed host cert (and CA cert if not there): localmksslcerts --cert --cacert IT-guide_dr_Jones_CA If issuer is not given, \"postmaster@\" is used." exit 1 } mkcerthash() { filebase="$1" filename="$filebase.pem" certhash="$(openssl x509 -noout -hash -in "$SSLCERTDIR/$filename")" hashfile="$certhash.0" ln -sf "$filename" "$SSLCERTDIR/$hashfile" } mkselfcert() { filebase="$1" domain="$2" filename="$filebase.pem" openssl req -new -x509 -nodes \ -days "$DAYS2EXPIRE" \ -keyout "$SSLCERTDIR/$filename" \ -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+ $cn $state $loc $org $ou $domain $issuer + mkcerthash "$filebase" chown root:root "$SSLCERTDIR/$filename" chmod 0640 "$SSLCERTDIR/$filename" } mkkey() { filebase="$1" openssl genrsa \ -out "$SSLKEYDIR/$filename" chown root:root "$SSLKEYDIR/$filename" chmod 0600 "$SSLKEYDIR/$filename" } mkcertreq() { filebase="$1" domain="$2" filename="$filebase.pem" openssl req -new \ -key "$SSLKEYDIR/$filename" \ -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+ $cn $state $loc $org $ou $domain $issuer + chown root:root "$SSLCERTDIR/$filename" chmod 0640 "$SSLCERTDIR/$filename" } mkselfcacert() { filebase="$1" domain="$2" cacert="$3" filename="$filebase.pem" reqfilename="$filebase.csr" cafilename="$cacert.pem" openssl x509 -req \ -days $DAYS2EXPIRE \ -CA "$SSLCERTDIR/$cafilename" \ -CAkey "$SSLKEYDIR/$cafilename" \ -CAcreateserial \ -in "$SSLCERTDIR/$reqfilename" \ -out "$SSLCERTDIR/$filename" } mkcacert() { filebase="$1" filename="$filebase.pem" #FIXME: Make strength configurable openssl genrsa -des3 \ -out "$SSLKEYDIR/$filename" 1024 chown root:root "$SSLKEYDIR/$filename" chmod 0400 "$SSLKEYDIR/$filename" # Generate and pre-fill certification request #FIXME: Make validity configurable openssl req -new \ -key "$SSLKEYDIR/$filename" \ -x509 -days 1095 \ -out "$SSLCERTDIR/$filename" # Add hash to certified public certificate and cleanup mkcerthash "$cacert" chown root:root "$SSLCERTDIR/$filename" chmod 0640 "$SSLCERTDIR/$filename" } fqdn='' cn='' state='' loc='' org='' ou='' daemon='' daemons='' issuer='' cert='' cacert='' makeca='' force='' args='' TEMP=`getopt -o f --long help,fqdn:,cn:,state:,loc:,org:,ou:,daemon:,issuer:,cert,cacert:,makeca,force -n "$prg" -- "$@"` # Check for non-GNU getopt if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi eval set -- "$TEMP" # TODO: Redesign to a case of selfcert, selfca or ca while true ; do case "$1" in --help) usage;; --fqdn) fqdn="$2"; shift 2;; --cn) cn="$2"; shift 2;; --state) state="$2"; shift 2;; --loc) loc="$2"; shift 2;; --org) org="$2"; shift 2;; --ou) ou="$2"; shift 2;; --daemon) daemons="$daemons$2 "; shift 2;; --issuer) issuer="$2"; shift 2;; --cert) cert=1; shift;; --cacert) cacert="$2"; shift 2;; --makeca) makeca=1; shift;; --force|-f) force=1; shift;; --) shift; break;; *) echo "Internal error!" ; exit 1 ;; esac done if [ -z "$issuer" ]; then DOMAINNAME="`hostname -d`" ISSUER="postmaster@$DOMAINNAME" fi if [ -z "$fqdn" ]; then if [ $# -gt 0 ]; then fqdn="`hostname -f`" else echo "Too few parameters!" usage fi fi for val in org ou; do if eval [ -z "\$$val" ]; then eval "$val=\"$fqdn\"" fi done for val in cn state loc; do if eval [ -z "\$$val" ]; then eval "$val=\".\"" fi done if [ -n "$cert" ]; then if [ ! -s "$SSLCERTDIR/$fqdn.pem" ] || [ ! -s "$SSLKEYDIR/$fqdn.pem" ]; then echo "WARNING: Host certificate for \"$fqdn\" missing..." if [ -z "$cacert" ]; then echo "ERROR: The \"--cacert\" option is required when making a host certificate!" exit 1 fi # Cleaning up - if allowed for file in "$SSLKEYDIR/$fqdn.pem" "$SSLCERTDIR/$fqdn.csr" "$SSLCERTDIR/$fqdn.pem"; do if [ -e "$file" ]; then if [ -n "$force" ]; then rm -f "$file" else echo "ERROR: File \"$file\" already exists!" exit 1 fi fi done if [ ! -s "$SSLCERTDIR/$cacert.pem" ] || [ ! -s "$SSLKEYDIR/$cacert.pem" ]; then echo "WARNING: CAcert (certifying authority certificate) missing..." if [ -z "$makeca" ]; then echo "ERROR: The \"--makeca\" option is required when making a CAcert!" exit 1 fi # Generate private key for CA certificate echo "Generating CAcert \"$cacert\"..." mkcacert "$cacert" fi echo "Generating host certificate for \"$fqdn\"..." # Generate private key for host certificate mkkey "$fqdn" # Generate and pre-fill certification request mkcertreq "$fqdn" "$fqdn" if [ -n "$cacert" ]; then # Generate public certificate from certification request mkselfcacert "$fqdn" "$fqdn" "$cacert" elif [ ! -f "$SSLCERTDIR/$fqdn.pem" ]; then echo "Certificate request generated: $SSLCERTDIR/$fqdn.csr" echo "Now pass the request to you certificate authority, save their" echo "provided certificate as \"$SSLCERTDIR/$fqdn.pem\"," echo "and run this script with same options again." exit 0 fi if [ ! -f "$SSLCERTDIR/$fqdn.pem" ]; then echo "ERROR: certificate "$SSLCERTDIR/$fqdn.pem" not found!" exit 1 fi # Add hash to certified public certificate and cleanup mkcerthash "$fqdn" rm "$SSLCERTDIR/$fqdn.csr" fi fi for daemon in $daemons $@; do if [ -f "$SSLCERTDIR/$daemon.pem" ]; then if [ -n "$force" ]; then rm -f "$SSLCERTDIR/$(openssl x509 -noout -hash < "$SSLCERTDIR/$daemon.pem").0" rm -f "$SSLCERTDIR/$daemon.pem" else echo "Ignoring certificate (/etc/ssl/certs/$daemon.pem already exists...)" continue fi fi if [ -n "$cert" ]; then echo "Attaching $daemon to certified certificate for $fqdn." ln -sf "$fqdn.pem" "$SSLCERTDIR/$daemon.pem" ln -sf "$fqdn.pem" "$SSLKEYDIR/$daemon.pem" else echo -n "Generating self-certifying $daemon certificate..." mkselfcert "$daemon" "$fqdn" echo "Done!" fi done