review code for checking certificates
[myplc.git] / plc.d / ssl
index 0615d75..f09294a 100755 (executable)
--- a/plc.d/ssl
+++ b/plc.d/ssl
@@ -1,14 +1,12 @@
 #!/bin/bash
 #
-# priority: 400
+# priority: 300
 #
 # Generate SSL certificates
 #
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: ssl,v 1.4 2006/04/25 21:18:19 mlhuang Exp $
-#
 
 # Source function library and configuration
 . /etc/plc.d/functions
 # Be verbose
 set -x
 
-mkcert ()
+# Print the CNAME of an SSL certificate
+ssl_cname ()
 {
-    CN=$1
-    KEY=$2
-    CRT=$3
-
-    # Generate a temporary CSR. We could save the CSR, but it's not
-    # worth the trouble.
-    csr=$(mktemp /tmp/csr.XXXXXX)
-
-    mkdir -p $(dirname $KEY)
-    openssl req -config /etc/planetlab/ssl/openssl.cnf \
-       -new -extensions v3_req -days 365 -set_serial $RANDOM \
-       -batch -subj "/CN=$CN" \
-       -nodes -keyout $KEY -out $csr
-    check
-    chmod 600 $KEY
-
-    # Generate and sign certificate from CSR
-    serial=$(cat /etc/planetlab/ssl/serial)
-
-    openssl ca -config /etc/planetlab/ssl/openssl.cnf \
-       -keyfile $PLC_ROOT_CA_SSL_KEY \
-       -cert $PLC_ROOT_CA_SSL_CRT \
-       -batch -infiles $csr
-    check
-
-    mv /etc/planetlab/ssl/$serial.pem $CRT
-    chmod 644 $CRT
-
-    # Delete CSR
-    rm -f $csr
+    openssl x509 -noout -in $1 -subject | \
+       sed -e 's|.*CN *= *\([-_a-zA-Z0-9.]*\).*|\1|' | \
+       lower
 }
 
-case "$1" in
-    start)
-       MESSAGE=$"Generating SSL certificates"
-       dialog "$MESSAGE"
-
-       # Check if root CA certificate is valid
-       if [ -f $PLC_ROOT_CA_SSL_CRT ] ; then
-           verify=$(openssl verify $PLC_ROOT_CA_SSL_CRT)
-           # If self signed, assume that we generated it
-           if grep -q "self signed certificate" <<<$verify ; then
-               # Delete if expired or PLC name or e-mail address has changed
-               if grep -q "expired" <<<$verify || \
-                   [ "$(ssl_cname $PLC_ROOT_CA_SSL_CRT)" != "$PLC_NAME Root CA" ] || \
-                   [ "$(ssl_email $PLC_ROOT_CA_SSL_CRT)" != "$PLC_MAIL_SUPPORT_ADDRESS" ] ; then
-                   rm -f $PLC_ROOT_CA_SSL_CRT
-               fi
-           fi
-       fi
+backup_file ()
+{
+    filepath=$1
+    filename=$(basename ${filepath})
+    dir=$(dirname ${filepath})
+    mv -f ${filepath} ${dir}/${filename}-`date +%Y-%m-%d-%H-%M-%S`.bak
+}
 
-       # Generate root CA key pair and certificate
-       if [ ! -f $PLC_ROOT_CA_SSL_CRT ] ; then
-           mkdir -p $(dirname $PLC_ROOT_CA_SSL_CRT)
-           openssl req -config /etc/planetlab/ssl/openssl.cnf \
-               -new -x509 -extensions v3_ca -days 3650 -set_serial $RANDOM \
-               -batch -subj "/CN=$PLC_NAME Root CA/emailAddress=$PLC_MAIL_SUPPORT_ADDRESS" \
-               -nodes -keyout $PLC_ROOT_CA_SSL_KEY -out $PLC_ROOT_CA_SSL_CRT
-           check
-           chmod 600 $PLC_ROOT_CA_SSL_KEY
-           chmod 644 $PLC_ROOT_CA_SSL_CRT
-
-           # API certificate verification requires a public key
-           openssl rsa -pubout <$PLC_ROOT_CA_SSL_KEY >$PLC_ROOT_CA_SSL_KEY_PUB
-           check
-           chmod 644 $PLC_ROOT_CA_SSL_KEY_PUB
-
-           # Reset DB
-           >/etc/planetlab/ssl/index.txt
-           echo "01" >/etc/planetlab/ssl/serial
+# Verify a certificate. If invalid, generate a new self-signed
+# certificate.
+verify_or_generate_certificate() {
+    crt=$1
+    key=$2
+    ca=$3
+    cname=$(lower $4)
+
+    # If the CA certificate does not exist, assume that the
+    # certificate is self-signed.
+    if [ ! -f $ca ] ; then
+       cp -a $crt $ca
+    fi
+
+    if [ -f $crt ] ; then
+       # Check if certificate is valid
+       # Backup if invalid or if the subject has changed
+       if openssl verify -CAfile $ca $crt | grep -q "error" || \
+           [ "$(ssl_cname $crt)" != "$cname" ] ; then
+            backup_file $crt
+            backup_file $ca
+            backup_file $key
        fi
+    fi
 
-       # Check if MA/SA certificate is valid
-       if [ -f $PLC_MA_SA_SSL_CRT ] ; then
-           verify=$(openssl verify -CAfile $PLC_ROOT_CA_SSL_CRT $PLC_MA_SA_SSL_CRT)
-           # Delete if expired or not signed correctly
-           if grep -q "error" <<<$verify ; then
-               rm -f $PLC_MA_SA_SSL_CRT
-           fi
+    if [ ! -f $crt ] ; then
+        # Set subject
+       subj=
+       if [ -n "$cname" ] ; then
+           subj="$subj/CN=$cname"
        fi
 
-       # Generate MA/SA key pair and certificate
-       if [ ! -f $PLC_MA_SA_SSL_CRT ] ; then
-           mkcert "$PLC_NAME Management and Slice Authority" \
-               $PLC_MA_SA_SSL_KEY $PLC_MA_SA_SSL_CRT
+       # Generate new self-signed certificate
+       mkdir -p $(dirname $crt)
+       openssl req -new -x509 -days 3650 -set_serial $RANDOM \
+           -batch -subj "$subj" \
+           -nodes -keyout $key -out $crt
+       check
 
-           # API requires a public key for slice ticket verification
-           openssl rsa -pubout <$PLC_MA_SA_SSL_KEY >$PLC_MA_SA_SSL_KEY_PUB
-           check
-           chmod 644 $PLC_MA_SA_SSL_KEY_PUB
-       fi
+       # The certificate it self-signed, so it is its own CA
+       cp -a $crt $ca
+    fi
 
-       # Generate HTTPS certificate(s). We generate a certificate for
-       # each enabled server with a different hostname.
-       for server in WWW API BOOT ; do
-           ssl_key=PLC_${server}_SSL_KEY
+    # Fix permissions
+    chmod 644 $crt $ca
+}
+
+case "$1" in
+    start)
+
+       # Generate HTTPS certificates if necessary. We generate a
+       # certificate for each enabled server with a different
+       # hostname. These self-signed certificates may be overridden
+       # later.
+        MESSAGE=$"Generating SSL certificates for"
+        dialog "$MESSAGE"
+
+       for server in WWW API BOOT MONITOR; do
+           eval "a=\$PLC_${server}_ENABLED"
+            echo $a
+            if [ "$a" -ne 1 ] ; then
+               echo "Skipping"
+                continue
+            fi
+           dialog "$server"
+            ssl_key=PLC_${server}_SSL_KEY
            ssl_crt=PLC_${server}_SSL_CRT
+           ca_ssl_crt=PLC_${server}_CA_SSL_CRT
            hostname=PLC_${server}_HOST
 
            # Check if we have already generated a certificate for
            # the same hostname.
-           for previous_server in WWW API BOOT ; do
+           for previous_server in WWW API BOOT MONITOR; do
                if [ "$server" = "$previous_server" ] ; then
                    break
                fi
                previous_ssl_key=PLC_${previous_server}_SSL_KEY
                previous_ssl_crt=PLC_${previous_server}_SSL_CRT
+               previous_ca_ssl_crt=PLC_${previous_server}_CA_SSL_CRT
                previous_hostname=PLC_${previous_server}_HOST
 
                if [ -f ${!previous_ssl_crt} ] && \
                    [ "$(ssl_cname ${!previous_ssl_crt})" = "${!hostname}" ] ; then
                    cp -a ${!previous_ssl_key} ${!ssl_key}
                    cp -a ${!previous_ssl_crt} ${!ssl_crt}
+                   cp -a ${!previous_ca_ssl_crt} ${!ca_ssl_crt}
                    break
                fi
            done
 
-           # Check if certificate is valid
-           if [ -f ${!ssl_crt} ] ; then
-               verify=$(openssl verify -CAfile $PLC_ROOT_CA_SSL_CRT ${!ssl_crt})
-               # Delete if expired or hostname changed. These
-               # certificates do not necessarily have to be signed by
-               # the root CA; they may be signed by a third party,
-               # e.g., Entrust or Verisign.
-               if grep -q "expired" <<<$verify || \
-                   [ "$(ssl_cname ${!ssl_crt})" != "${!hostname}" ] ; then
-                   rm -f ${!ssl_crt}
-               fi
-           fi
-
-           # Generate and sign certificate
-           if [ ! -f ${!ssl_crt} ] ; then
-               mkcert ${!hostname} ${!ssl_key} ${!ssl_crt}
-           fi
+           verify_or_generate_certificate \
+               ${!ssl_crt} ${!ssl_key} ${!ca_ssl_crt} \
+               ${!hostname}
        done
 
        # Install HTTPS certificates into both /etc/pki (Fedora Core
        # 4) and /etc/httpd/conf (Fedora Core 2). If the API, boot,
        # and web servers are all running on the same machine, the web
        # server certificate takes precedence.
-       for server in API BOOT WWW ; do
+       for server in API BOOT MONITOR WWW; do
            enabled=PLC_${server}_ENABLED
            if [ "${!enabled}" != "1" ] ; then
                continue
            fi
            ssl_key=PLC_${server}_SSL_KEY
            ssl_crt=PLC_${server}_SSL_CRT
+           ssl_ca_crt=PLC_${server}_CA_SSL_CRT
 
            symlink ${!ssl_crt} /etc/pki/tls/certs/localhost.crt
            symlink ${!ssl_key} /etc/pki/tls/private/localhost.key
+           symlink ${!ssl_ca_crt} /etc/pki/tls/certs/server-chain.crt
            symlink ${!ssl_crt} /etc/httpd/conf/ssl.crt/server.crt
            symlink ${!ssl_key} /etc/httpd/conf/ssl.key/server.key
        done
 
+       # Ensure that the server-chain gets used, as it is off by
+       # default.
+       sed -i -e 's/^#SSLCertificateChainFile /SSLCertificateChainFile /' \
+           /etc/httpd/conf.d/ssl.conf
+
        result "$MESSAGE"
        ;;
 esac