#!/bin/sh # # /usr/local/sbin/localmksslcerts # Copyright 2001-2005 Jonas Smedegaard # # $Id: localmksslcerts,v 1.22 2005-10-20 17:13:25 jonas Exp $ # # Generate certificates for mail (and other) servers # Based on uw-imapd-ssl post-install script # # TODO: Add symlink from CA certificate to cacert.pem if non-existing set -e prg=$(basename $0) copyright="(C) 2001-2005 Jonas Smedegaard " # Set some defaults PATH="$PATH:/usr/bin/ssl" DAYS2EXPIRE="365" SSLCERTDIR="/etc/ssl/certs" SSLKEYDIR="/etc/ssl/private" HOSTNAME="$(hostname -f)" DOMAINNAME="$(hostname -d)" CN="." STATE="." LOC="." ORG="@@HOSTNAME@@" OU="@@HOSTNAME@@" FQDN="@@HOSTNAME@@" ISSUER="postmaster@@@DOMAINNAME@@" if [ -f /etc/local/mksslcerts.conf ]; then . /etc/local/mksslcerts.conf fi for var in CN STATE LOC ORG OU FQDN ISSUER; do eval $var=\"\$\(echo \"\$$var\" \| /bin/sed -e \"s/@@HOSTNAME@@/$HOSTNAME/g\" -e \"s/@@DOMAINNAME@@/$DOMAINNAME/g\"\)\"; done mkcerthash() { base="$1" certfile="$base.pem" certhash="$(openssl x509 -noout -hash -in "$SSLCERTDIR/$certfile")" ln -sf "$certfile" "$SSLCERTDIR/$certhash.0" } mkselfcert() { base="$1" domain="$2" keypath="$SSLKEYDIR/$base.pem" certpath="$SSLCERTDIR/$base.pem" openssl req -new -x509 -nodes \ -days "$DAYS2EXPIRE" \ -keyout "$keypath" \ -out "$certpath" > /dev/null 2>&1 <<+ $cn $state $loc $org $ou $domain $issuer + chown root:root "$keypath" "$certpath" chmod 0600 "$keypath" chmod 0644 "$certpath" mkcerthash "$base" } mkcertreq() { base="$1" domain="$2" keypath="$SSLKEYDIR/$base.pem" reqpath="$SSLCERTDIR/$base.csr" openssl req -nodes -new \ -keyout "$keypath" \ -out "$reqpath" > /dev/null 2>&1 <<+ $cn $state $loc $org $ou $domain $issuer . . + chown root:root "$reqpath" chmod 0640 "$reqpath" } mkselfkey() { filebase="$1" keypath="$SSLKEYDIR/$filebase.pem" openssl genrsa \ -out "$keypath" > /dev/null 2>&1 chown root:root "$keypath" chmod 0600 "$keypath" } mkselfcertreq() { base="$1" domain="$2" keypath="$SSLKEYDIR/$base.pem" reqpath="$SSLCERTDIR/$base.csr" openssl req -new \ -key "$keypath" \ -out "$reqpath" > /dev/null 2>&1 <<+ $cn $state $loc $org $ou $domain $issuer . . + chown root:root "$reqpath" chmod 0640 "$reqpath" } mkselfcacert() { base="$1" domain="$2" cacert="$3" certpath="$SSLCERTDIR/$base.pem" reqpath="$SSLCERTDIR/$base.csr" cakeypath="$SSLKEYDIR/$cacert.pem" cacertpath="$SSLCERTDIR/$cacert.pem" openssl x509 -req \ -days $DAYS2EXPIRE \ -CA "$cacertpath" \ -CAkey "$cakeyfile" \ -CAcreateserial \ -in "$reqpath" \ -out "$certpath" chown root:root "$certpath" chmod 0644 "$certpath" mkcerthash "$base" } mkcacert() { base="$1" cakeypath="$SSLKEYDIR/$base.pem" cacertpath="$SSLCERTDIR/$base.pem" #FIXME: Make strength configurable openssl genrsa -des3 \ -out "$cakeypath" 1024 chown root:root "$cakeypath" chmod 0400 "$cakeypath" # Generate and pre-fill certification request #FIXME: Make validity configurable openssl req -new \ -key "$cakeypath" \ -x509 -days 1095 \ -out "$cacertpath" # Add hash to certified public certificate and cleanup chown root:root "$cacertpath" chmod 0640 "$cacertpath" mkcerthash "$base" } usage() { cat < [...] [--force] or: $prg [...] [-f] General options: --type Type of certificate selfcert Self-certified certificate ca Use Certificate Authority selfca Homemade Certificate Authority Default: try guess based on other options --daemon Daemon(s) in need for a certificate Default: none (or create only CA cert) (selfcert certs are unique per daemon) (ca and selfca are shared per hostname) -f, --force Force overwriting existing certificate(s) -h, --help This help text General certificate options: --fqdn Fully Qualified Domain Name Default: $FQDN --cn Country Name (2 letter code) Default: $CN --state State or Province Name (full name) Default: $STATE --loc Locality Name (eg, city) Default: $LOC --org Organisation/company Default: $ORG --ou Organisational unit/department Default: $OU --issuer Email address of entity issuing certificate Default: $ISSUER Selfcert options: --cacert Certificate Authority to use/create Default: \"cacert\" (possibly symlinked) --makeca Create CA certificate if missing Examples: Create certs for UW imapd/popd, guessing hostname and certificate type: $prg imapd pop3d Create host cert for non-default hostname and bind to UW daemons: $prg --type selfca --fqdn mail.$DOMAINNAME imapd pop3d Create local CA, signed host cert for $HOSTNAME and links to UW daemons: $prg --type selfca --cacert My-own_CA --makeca imapd pop3d Create certificate request to pass to Certificate Authority $prg --type ca --fqdn mail.$DOMAINNAME Finish install of CA-certified host certificate $prg --type ca --fqdn mail.$DOMAINNAME imapd pop3d EOF exit 1 } certtype='' fqdn='' cn='' state='' loc='' org='' ou='' daemon='' daemons='' issuer='' cacert='' makeca='' force='' TEMP=`getopt -o fh --long type:,daemon:,force,help,fqdn:,cn:,state:,loc:,org:,ou:,issuer:,cacert:,makeca -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 --type) case "$2" in selfcert|ca|selfca) certtype="$2"; shift 2;; *) echo "ERROR: Wrong certificate type: \"$2\"!" >&2; usage;; esac;; --daemon) daemons="$daemons$2 "; shift 2;; --force|-f) force=1; shift;; --help|-h) 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;; --issuer) issuer="$2"; shift 2;; --cacert) cacert="$2"; shift 2;; --makeca) makeca=1; shift;; --) shift; break;; *) echo "Internal error!" >&2 ; exit 1 ;; esac done # Non-opt arguments are daemons daemons="$daemons $@" # Try to pick a sane certificate type if not explicitly set if [ -z "$certtype" ] && [ -n "$makeca" ]; then certtype="selfca" fi if [ -z "$certtype" ] && [ -n "$cacert" ] && [ -r "$SSLKEYDIR/$cacert.pem" ]; then certtype="selfca" fi if [ -z "$certtype" ]; then echo "ERROR: Cannot guess certificate type!" >&2 echo " Please explicitly set --type (or more other options)." >&2 exit 1 fi # Use defaults for empty vars, and export cn="${cn:-$CN}" state="${state:-$STATE}" loc="${loc:-$LOC}" org="${org:-$ORG}" ou="${ou:-$OU}" fqdn="${fqdn:-$FQDN}" issuer="${issuer:-$ISSUER}" # case "$certtype" in selfcert) ;; ca) if [ ! -f "$SSLCERTDIR/$fqdn.pem" ] || [ -n "$force" ]; then echo "Generating host key and certificate request for \"$fqdn\"..." mkcertreq "$fqdn" "$fqdn" echo "Certificate request generated: $SSLCERTDIR/$fqdn.csr!" echo echo "Now pass this request to you certificate authority, save their" echo "provided certificate as \"$SSLCERTDIR/$fqdn.pem\"," echo "and run this script again with same options (except --force)." exit 0 else echo "Hashing new certificate and cleanup..." mkcerthash "$fqdn" rm -f "$SSLCERTDIR/$fqdn.csr" fi ;; selfca) if [ -z "$cacert" ] || [ "$cacert" = "cacert" ]; then if ! cacert="$(basename "$(readlink "$SSLCERTDIR/cacert.pem")")"; then #" mc syntax hilite bug workaround echo "ERROR: Cannot resolve default CA cert \"$SSLCERTDIR/cacert.pem\"" >&2 exit 1 fi fi if [ ! -s "$SSLCERTDIR/$fqdn.pem" ] || [ ! -s "$SSLKEYDIR/$fqdn.pem" ]; then if [ -n "$force" ]; then echo "WARNING: Host certificate missing - will be (re)created!" >&2 else echo "Error: Host certificate is missing!" >&2 echo " Use \"--cacert\" to (re)create." >&2 exit 1 fi fi # Cleaning up - if allowed for file in "$SSLKEYDIR/$fqdn.pem" "$SSLCERTDIR/$fqdn.csr" "$SSLCERTDIR/$fqdn.pem"; do if [ -n "$force" ] && [ -e "$file" ]; then rm -f "$file" else echo "ERROR: File \"$file\" already exists!" >&2 echo " Use \"--force\" to override." >&2 exit 1 fi done if [ ! -s "$SSLCERTDIR/$cacert.pem" ] || [ ! -s "$SSLKEYDIR/$cacert.pem" ]; then if [ -z "$makeca"]; then echo "Error: CA certificate is missing!" >&2 echo " Use another \"--cacert\" or create with \"--makeca\"." >&2 exit 1 fi echo "Generating CAcert \"$cacert\"..." mkcacert "$cacert" fi echo "Generating host key for \"$fqdn\"..." mkselfkey "$fqdn" echo "Generating host certificate for \"$fqdn\"..." mkselfcertreq "$fqdn" "$fqdn" mkselfcacert "$fqdn" "$fqdn" "$cacert" rm "$SSLCERTDIR/$fqdn.csr" ;; *) echo "Internal error!" >&2 exit 1 ;; esac for daemon in $daemons; do if [ -f "$SSLCERTDIR/$daemon.pem" ]; then if [ -n "$force" ]; then echo "WARNING: Removing exisitng certificate \"/etc/ssl/certs/$daemon.pem\"." >&2 rm -f "$SSLCERTDIR/$(openssl x509 -noout -hash < "$SSLCERTDIR/$daemon.pem").0" rm -f "$SSLCERTDIR/$daemon.pem" else echo "WARNING: Ignoring already existing certificate \"/etc/ssl/certs/$daemon.pem\"." >&2 continue fi fi case "$certtype" in selfcert) echo -n "Generating self-certifying $daemon certificate..." mkselfcert "$daemon" "$fqdn" echo "Done!" ;; ca|selfca) echo "Attaching $daemon to certified certificate for $fqdn." ln -sf "$fqdn.pem" "$SSLCERTDIR/$daemon.pem" ln -sf "$fqdn.pem" "$SSLKEYDIR/$daemon.pem" ;; *) echo "Internal error!" >&2 exit 1 ;; esac done