summaryrefslogtreecommitdiff
path: root/localmksslcerts
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2005-10-19 19:07:19 +0000
committerJonas Smedegaard <dr@jones.dk>2005-10-19 19:07:19 +0000
commit88f0d87066880385f5fe9173f98b65b55818525b (patch)
treee4972ef5bb1f384a0e6c4769237978463e7f2e7f /localmksslcerts
parent7f947e04e2a8c7dcfbfe36aeec52af5caf58ff05 (diff)
Major rewrite to support non-local CA cert handling.
Diffstat (limited to 'localmksslcerts')
-rwxr-xr-xlocalmksslcerts394
1 files changed, 233 insertions, 161 deletions
diff --git a/localmksslcerts b/localmksslcerts
index bf65432..742a04a 100755
--- a/localmksslcerts
+++ b/localmksslcerts
@@ -1,85 +1,60 @@
#!/bin/sh
#
# /usr/local/sbin/localmksslcerts
-# Copyright 2001-2004 Jonas Smedegaard <dr@jones.dk>
+# Copyright 2001-2005 Jonas Smedegaard <dr@jones.dk>
#
-# $Id: localmksslcerts,v 1.20 2005-10-18 12:32:02 jonas Exp $
+# $Id: localmksslcerts,v 1.21 2005-10-19 19:07:19 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 <dr@jones.dk>"
+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)"
-usage() {
-echo "$prg, $copyright
-
-Usage: $prg [--fqdn <FQDN>] [...] --daemon <daemon> [...] [--force]
- or: $prg <daemon> [<daemon>...] [-f]
-
-Options:
- --fqdn <FQDN> Fully Qualified Domain Name for this host.
- --cn <country> Country Name (2 letter code)
- --state <state> State or Province Name (full name)
- --loc <locality> Locality Name (eg, city)
- --org <organisation> Organisation/company
- --ou <department> Organisational unit/department
- --daemon <daemon> Daemon(s) in need for a certificate
- (certificate is generated for each daemon)
- --issuer <issuer> Email address of entity issuing certificate
- --cert Use certified host certificate
- --cacert <file> 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
+CN="."
+STATE="."
+LOC="."
+ORG="@@HOSTNAME@@"
+OU="@@HOSTNAME@@"
+FQDN="@@HOSTNAME@@"
+ISSUER="postmaster@@@DOMAINNAME@@"
+if [ -f /etc/local/mksslcerts.conf ]; then
+ . /etc/local/mksslcerts.conf
+fi
-If issuer is not given, \"postmaster@<localdomain>\" is used."
-exit 1
-}
+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() {
- filebase="$1"
- filename="$filebase.pem"
- certhash="$(openssl x509 -noout -hash -in "$SSLCERTDIR/$filename")"
- hashfile="$certhash.0"
- ln -sf "$filename" "$SSLCERTDIR/$hashfile"
+ base="$1"
+ certfile="$base.pem"
+ certhash="$(openssl x509 -noout -hash -in "$SSLCERTDIR/$certfile")"
+ ln -sf "$certfile" "$SSLCERTDIR/$certhash.0"
}
mkselfcert() {
- filebase="$1"
+ base="$1"
domain="$2"
- filename="$filebase.pem"
+ keypath="$SSLKEYDIR/$base.pem"
+ certpath="$SSLCERTDIR/$base.pem"
openssl req -new -x509 -nodes \
-days "$DAYS2EXPIRE" \
- -keyout "$SSLCERTDIR/$filename" \
- -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+
+ -keyout "$keypath" \
+ -out "$certpath" > /dev/null 2>&1 <<+
$cn
$state
$loc
@@ -88,26 +63,29 @@ $ou
$domain
$issuer
+
- mkcerthash "$filebase"
- chown root:root "$SSLCERTDIR/$filename"
- chmod 0640 "$SSLCERTDIR/$filename"
+ chown root:root "$keypath" "$certpath"
+ chmod 0600 "$keypath"
+ chmod 0644 "$certpath"
+ mkcerthash "$base"
}
mkkey() {
filebase="$1"
+ keypath="$SSLKEYDIR/$filebase.pem"
openssl genrsa \
- -out "$SSLKEYDIR/$filename"
- chown root:root "$SSLKEYDIR/$filename"
- chmod 0600 "$SSLKEYDIR/$filename"
+ -out "$keypath" > /dev/null 2>&1
+ chown root:root "$keypath"
+ chmod 0600 "$keypath"
}
mkcertreq() {
- filebase="$1"
+ base="$1"
domain="$2"
- filename="$filebase.pem"
+ keypath="$SSLKEYDIR/$base.pem"
+ reqpath="$SSLCERTDIR/$base.csr"
openssl req -new \
- -key "$SSLKEYDIR/$filename" \
- -out "$SSLCERTDIR/$filename" > /dev/null 2>&1 <<+
+ -key "$keypath" \
+ -out "$reqpath" > /dev/null 2>&1 <<+
$cn
$state
$loc
@@ -115,47 +93,116 @@ $org
$ou
$domain
$issuer
+.
+.
+
- chown root:root "$SSLCERTDIR/$filename"
- chmod 0640 "$SSLCERTDIR/$filename"
+ chown root:root "$reqpath"
+ chmod 0640 "$reqpath"
}
mkselfcacert() {
- filebase="$1"
+ base="$1"
domain="$2"
cacert="$3"
- filename="$filebase.pem"
- reqfilename="$filebase.csr"
- cafilename="$cacert.pem"
+ certpath="$SSLCERTDIR/$base.pem"
+ reqpath="$SSLCERTDIR/$base.csr"
+ cakeypath="$SSLKEYDIR/$cacert.pem"
+ cacertpath="$SSLCERTDIR/$cacert.pem"
openssl x509 -req \
-days $DAYS2EXPIRE \
- -CA "$SSLCERTDIR/$cafilename" \
- -CAkey "$SSLKEYDIR/$cafilename" \
+ -CA "$cacertpath" \
+ -CAkey "$cakeyfile" \
-CAcreateserial \
- -in "$SSLCERTDIR/$reqfilename" \
- -out "$SSLCERTDIR/$filename"
+ -in "$reqpath" \
+ -out "$certpath"
+ chown root:root "$certpath"
+ chmod 0644 "$certpath"
+ mkcerthash "$base"
}
mkcacert() {
- filebase="$1"
- filename="$filebase.pem"
+ base="$1"
+ cakeypath="$SSLKEYDIR/$base.pem"
+ cacertpath="$SSLCERTDIR/$base.pem"
#FIXME: Make strength configurable
openssl genrsa -des3 \
- -out "$SSLKEYDIR/$filename" 1024
- chown root:root "$SSLKEYDIR/$filename"
- chmod 0400 "$SSLKEYDIR/$filename"
+ -out "$cakeypath" 1024
+ chown root:root "$cakeypath"
+ chmod 0400 "$cakeypath"
# Generate and pre-fill certification request
#FIXME: Make validity configurable
openssl req -new \
- -key "$SSLKEYDIR/$filename" \
+ -key "$cakeypath" \
-x509 -days 1095 \
- -out "$SSLCERTDIR/$filename"
+ -out "$cacertpath"
# Add hash to certified public certificate and cleanup
- mkcerthash "$cacert"
- chown root:root "$SSLCERTDIR/$filename"
- chmod 0640 "$SSLCERTDIR/$filename"
+ 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=''
@@ -165,13 +212,11 @@ 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" -- "$@"`
+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
@@ -182,120 +227,147 @@ eval set -- "$TEMP"
while true ; do
case "$1" in
- --help) usage;;
+ --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;;
- --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 ;;
+ *) echo "Internal error!" >&2 ; exit 1 ;;
esac
done
-if [ -z "$issuer" ]; then
- DOMAINNAME="`hostname -d`"
- ISSUER="postmaster@$DOMAINNAME"
-fi
+# Non-opt arguments are daemons
+daemons="$daemons $@"
-if [ -z "$fqdn" ]; then
- if [ $# -gt 0 ]; then
- fqdn="`hostname -f`"
- else
- echo "Too few parameters!"
- usage
- fi
+# 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
-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
+# 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}"
-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
+#
+case "$certtype" in
+ selfcert)
+ ;;
+ ca)
+ if [ ! -f "$SSLCERTDIR/$fqdn.pem" ] || [ -n "$force" ]; then
+ echo "Generating host key for \"$fqdn\"..."
+ mkkey "$fqdn"
+ echo "Generating 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 [ -e "$file" ]; then
- if [ -n "$force" ]; then
- rm -f "$file"
- else
- echo "ERROR: File \"$file\" already exists!"
- exit 1
- fi
+ 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
- echo "WARNING: CAcert (certifying authority certificate) missing..."
- if [ -z "$makeca" ]; then
- echo "ERROR: The \"--makeca\" option is required when making a CAcert!"
+ if [ -z "$makeca"]; then
+ echo "Error: CA certificate is missing!" >&2
+ echo " Use another \"--cacert\" or create with \"--makeca\"." >&2
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
+ echo "Generating host key for \"$fqdn\"..."
mkkey "$fqdn"
- # Generate and pre-fill certification request
+ echo "Generating host certificate for \"$fqdn\"..."
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"
+ mkselfcacert "$fqdn" "$fqdn" "$cacert"
rm "$SSLCERTDIR/$fqdn.csr"
- fi
-fi
+ ;;
+ *)
+ echo "Internal error!" >&2
+ exit 1
+ ;;
+esac
-for daemon in $daemons $@; do
+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 "Ignoring certificate (/etc/ssl/certs/$daemon.pem already exists...)"
+ echo "WARNING: Ignoring already existing certificate \"/etc/ssl/certs/$daemon.pem\"." >&2
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
+ 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