- #!/bin/sh
- #
- # /usr/local/sbin/localmksslcerts
- # Copyright 2001-2005 Jonas Smedegaard <dr@jones.dk>
- #
- # $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 <dr@jones.dk>"
- # 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 <<EOF
- $prg, $copyright
- Usage: $prg [--type selfcert|ca|selfca] [...] --daemon <daemon> [...] [--force]
- or: $prg <daemon> [<daemon>...] [-f]
- General options:
- --type <CERT_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> 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 <FQDN> Fully Qualified Domain Name
- Default: $FQDN
- --cn <country> Country Name (2 letter code)
- Default: $CN
- --state <state> State or Province Name (full name)
- Default: $STATE
- --loc <locality> Locality Name (eg, city)
- Default: $LOC
- --org <organisation> Organisation/company
- Default: $ORG
- --ou <department> Organisational unit/department
- Default: $OU
- --issuer <issuer> Email address of entity issuing certificate
- Default: $ISSUER
- Selfcert options:
- --cacert <file> 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
|