#!/bin/sh # # /usr/local/sbin/localmksslcerts # Copyright 2001-2004 Jonas Smedegaard # # $Id: localmksslcerts,v 1.19 2005-10-18 00:24:33 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" SSLPRIVDIR="/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" cn="$2" state="$3" loc="$4" org="$5" ou="$6" fqdn="$7" issuer="$8" 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 $fqdn $issuer + mkcerthash "$filebase" chown root:root "$SSLCERTDIR/$filename" chmod 0640 "$SSLCERTDIR/$filename" } mkcertreq() { filebase="$1" cn="$2" state="$3" loc="$4" org="$5" ou="$6" fqdn="$7" issuer="$8" filename="$filebase.pem" openssl req -new \ -key "$SSLPRIVDIR/$filename" \ -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+ $cn $state $loc $org $ou $fqdn $issuer + 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" 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 "$SSLPRIVDIR/$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 "$SSLPRIVDIR/$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 "$SSLPRIVDIR/$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\"..." #FIXME: Make strength configurable openssl genrsa -des3 -out "$SSLPRIVDIR/$cacert.pem" 1024 chown root:root "$SSLPRIVDIR/$cacert.pem" chmod 0400 "$SSLPRIVDIR/$cacert.pem" # Generate and pre-fill certification request #FIXME: Make validity configurable openssl req -new \ -key "$SSLPRIVDIR/$cacert.pem" \ -x509 -days 1095 \ -out "$SSLCERTDIR/$cacert.pem" # Add hash to certified public certificate and cleanup mkcerthash $cacert fi echo "Generating host certificate for \"$fqdn\"..." # Generate private key for host certificate openssl genrsa -out "$SSLPRIVDIR/$fqdn.pem" chown root:root "$SSLPRIVDIR/$fqdn.pem" chmod 0600 "$SSLPRIVDIR/$fqdn.pem" # Generate and pre-fill certification request mkcertreq "$fqdn" "$cn" "$state" "$loc" "$org" "$ou" "$fqdn" "$issuer" # Generate public certificate from certification request openssl x509 -req \ -days $DAYS2EXPIRE \ -CA "$SSLCERTDIR/$cacert.pem" \ -CAkey "$SSLPRIVDIR/$cacert.pem" \ -CAcreateserial -out "$SSLCERTDIR/$fqdn.pem" -in "$SSLCERTDIR/$fqdn.csr" # 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" "$SSLPRIVDIR/$daemon.pem" else echo -n "Generating self-certifying $daemon certificate..." mkselfcert "$daemon" "$cn" "$state" "$loc" "$org" "$ou" "$fqdn" "$issuer" echo "Done!" fi done