From: Mark Huang <mlhuang@cs.princeton.edu>
Date: Mon, 10 Jul 2006 21:06:16 +0000 (+0000)
Subject: - think i finally understand ssl now
X-Git-Tag: planetlab-4_0-rc1~150
X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=c422d72098b31a8013b443663c4498afd80fd418;p=myplc.git

- think i finally understand ssl now
- allow CA to be configured for each ssl certificate set
- never do any root CA stuff. this is outside the scope of myplc. myplc
  now only generates self-signed certs (but supports replacement of the
  self-signed certs with real certs signed by another CA, as long as the
  CA is specified)
- self-sign the MA/SA SSL certificate (and by extension, the MA/SA API
  certificate)
---

diff --git a/api-config b/api-config
index 765da82..b7b4eee 100755
--- a/api-config
+++ b/api-config
@@ -6,7 +6,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: api-config,v 1.12 2006/05/30 15:06:20 mlhuang Exp $
+# $Id: api-config,v 1.13 2006/06/23 20:33:28 mlhuang Exp $
 #
 
 from plc_config import PLCConfiguration
@@ -28,9 +28,11 @@ def main():
         globals()[category_id] = dict(zip(variablelist.keys(),
                                        [variable['value'] for variable in variablelist.values()]))
 
-    # Get the issuer e-mail address of the root CA certificate
+    # Get the issuer e-mail address and public key from the root CA certificate
     root_ca_email = commands.getoutput("openssl x509 -in %s -noout -email" % \
-                                       plc['root_ca_ssl_crt'])
+                                       plc_ma_sa['ca_ssl_crt'])
+    root_ca_key_pub = commands.getoutput("openssl x509 -in %s -noout -pubkey" % \
+                                         plc_ma_sa['ca_ssl_crt'])
 
     # Verify API certificate
     if os.path.exists(plc_ma_sa['api_crt']):
@@ -38,36 +40,35 @@ def main():
         try:
             cert_xml = file(plc_ma_sa['api_crt']).read().strip()
             # Verify root CA signature
-            CertOps.authenticate_cert(cert_xml,
-                                      {root_ca_email:
-                                       file(plc['root_ca_ssl_key_pub']).read().strip()})
+            CertOps.authenticate_cert(cert_xml, {root_ca_email: root_ca_key_pub})
             # Check if MA/SA e-mail address has changed
             dom = xml.dom.minidom.parseString(cert_xml)
-            for issuer in dom.getElementsByTagName('issuer'):
-                if issuer.getAttribute('email') != plc_mail['support_address']:
+            for subject in dom.getElementsByTagName('subject'):
+                if subject.getAttribute('email') != plc_mail['support_address']:
                     raise Exception, "E-mail address '%s' in certificate '%s' does not match support address '%s'" % \
-                          (issuer.getAttribute('email'), plc_ma_sa['api_crt'], plc_mail['support_address'])
+                          (subject.getAttribute('email'), plc_ma_sa['api_crt'], plc_mail['support_address'])
         except Exception, e:
             # Delete invalid API certificate
             print "Warning: ", e
             os.unlink(plc_ma_sa['api_crt'])
 
-    # Generate API certificate
+    # Generate self-signed API certificate
     if not os.path.exists(plc_ma_sa['api_crt']):
         print "Generating new API certificate"
         try:
             cert = Certificate.Certificate('ticket-cert-0')
-            ma_sa_ssl_key_pub = file(plc_ma_sa['ssl_key_pub']).read().strip()
+            ma_sa_ssl_key_pub = commands.getoutput("openssl x509 -in %s -noout -pubkey" % \
+                                                   plc_ma_sa['ssl_crt'])
             cert.add_subject_pubkey(pubkey = ma_sa_ssl_key_pub, email = plc_mail['support_address'])
             root_ca_subject = commands.getoutput("openssl x509 -in %s -noout -subject" % \
-                                                 plc['root_ca_ssl_crt'])
+                                                 plc_ma_sa['ssl_crt'])
             m = re.search('/CN=([^/]*).*', root_ca_subject)
             if m is None:
-                root_ca_cn = plc['name'] + " Root CA"
+                root_ca_cn = plc['name'] + " Management and Slice Authority"
             else:
                 root_ca_cn = m.group(1)
             cert.set_issuer(email = root_ca_email, cn = root_ca_cn)
-            cert_xml = cert.sign(plc['root_ca_ssl_key'])
+            cert_xml = cert.sign(plc_ma_sa['ssl_key'])
             ma_sa_api_crt = file(plc_ma_sa['api_crt'], "w")
             ma_sa_api_crt.write(cert_xml)
             ma_sa_api_crt.close()
@@ -96,7 +97,7 @@ def main():
                      'MA_SA_NAMESPACE': plc_ma_sa['namespace'],
                      'SESSION_LENGTH_HOURS': "24",
                      'ROOT_CA_EMAIL': root_ca_email,
-                     'ROOT_CA_PUB_KEY': plc['root_ca_ssl_key_pub'],
+                     'ROOT_CA_PUB_KEY': plc_ma_sa['ca_ssl_key_pub'],
                      'API_CERT_PATH': plc_ma_sa['api_crt'],
                      'MA_SA_PRIVATE_KEY': plc_ma_sa['ssl_key'],
                      'PL_API_TICKET_KEY_FILE': plc_ma_sa['ssl_key']}
diff --git a/plc.d/ssl b/plc.d/ssl
index d8da402..a4afb7f 100755
--- a/plc.d/ssl
+++ b/plc.d/ssl
@@ -7,7 +7,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: ssl,v 1.6 2006/06/28 20:44:17 alk Exp $
+# $Id: ssl,v 1.7 2006/06/28 21:34:18 mlhuang Exp $
 #
 
 # Source function library and configuration
@@ -17,112 +17,96 @@
 # 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 3650 -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 -n -e 's@.*/CN=\([^/]*\).*@\1@p'
 }
 
-case "$1" in
-    start)
-	MESSAGE=$"Generating SSL certificates"
-	dialog "$MESSAGE"
+# Print the emailAddress of an SSL certificate
+ssl_email ()
+{
+    openssl x509 -noout -in $1 -subject | \
+	sed -n -e 's@.*/emailAddress=\([^/]*\).*@\1@p'
+}
 
-	# 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
+# Verify a certificate. If invalid, generate a new self-signed
+# certificate.
+verify_or_generate_certificate() {
+    crt=$1
+    key=$2
+    ca=$3
+    cname=$4
+    email=$5
+
+    if [ -f $crt ] ; then
+	# Check if certificate is valid
+	verify=$(openssl verify -CAfile $ca $crt)
+	# Delete if invalid or if the subject has changed
+	if grep -q "error" <<<$verify || \
+	    [ "$(ssl_cname $crt)" != "$cname" ] || \
+	    [ "$(ssl_email $crt)" != "$email" ] ; then
+	    rm -f $crt $ca
 	fi
+    fi
 
-	# 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
+    if [ ! -f $crt ] ; then
+        # Set subject
+	subj=
+	if [ -n "$cname" ] ; then
+	    subj="$subj/CN=$cname"
 	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 [ -n "$email" ] ; then
+	    subj="$subj/emailAddress=$email"
 	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
-
-	    # Make readable by apache so that the API can sign certificates
-	    chown apache $PLC_MA_SA_SSL_KEY
-	    chmod 600 $PLC_MA_SA_SSL_KEY
+	# 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
+	chmod 644 $crt
+    fi
+
+    if [ ! -f $ca ] ; then
+        # The certificate it self-signed, so it is its own CA
+	cp -a $crt $ca
+    fi
+}
 
-	    # 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
+case "$1" in
+    start)
+	MESSAGE=$"Generating SSL certificates"
+	dialog "$MESSAGE"
 
-	# Generate self-signed HTTPS certificate(s). These nice
-	# commands come from the mod_ssl spec file for Fedora Core
-	# 2. We generate a certificate for each enabled server
-	# with a different hostname. These self-signed
-	# certificates may be overridden later.
+	# Verify or generate MA/SA certificate if necessary. This
+	# self-signed certificate may be overridden later.
+	verify_or_generate_certificate \
+	    $PLC_MA_SA_SSL_CRT $PLC_MA_SA_SSL_KEY $PLC_MA_SA_CA_SSL_CRT \
+	    "$PLC_NAME Management and Slice Authority" \
+	    $PLC_MAIL_SUPPORT_ADDRESS
+
+	# Make MA/SA key readable by apache so that the API can sign
+	# certificates
+	chown apache $PLC_MA_SA_SSL_KEY
+	chmod 600 $PLC_MA_SA_SSL_KEY
+
+	# Extract the public key of the root CA (if any) that signed
+	# the MA/SA certificate.
+	openssl x509 -in $PLC_MA_SA_CA_SSL_CRT -noout -pubkey >$PLC_MA_SA_CA_SSL_KEY_PUB
+	check
+	chmod 644 $PLC_MA_SA_CA_SSL_KEY_PUB
+
+	# 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.
 	for server in WWW API BOOT ; do
 	    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
@@ -133,38 +117,22 @@ case "$1" in
 		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 self-signed certificate is valid
-	    if [ -f ${!ssl_crt} ] ; then
-		verify=$(openssl verify ${!ssl_crt})
-		# If self-signed
-		if grep -q "self signed certificate" <<<$verify ; then
-		    # Delete if expired or hostname changed
-		    if grep -q "expired" <<<$verify || \
-			[ "$(ssl_cname ${!ssl_crt})" != "${!hostname}" ] ; then
-			rm -f ${!ssl_crt}
-		    fi
-		fi
-	    fi
+	    verify_or_generate_certificate \
+		${!ssl_crt} ${!ssl_key} ${!ca_ssl_crt} \
+		${!hostname} $PLC_MAIL_SUPPORT_ADDRESS
 
-	    # Generate new self-signed certificate
-	    if [ ! -f ${!ssl_crt} ] ; then
-		mkdir -p $(dirname ${!ssl_crt})
-		openssl req -new -x509 -days 3650 -set_serial $RANDOM \
-		    -batch -subj "/CN=${!hostname}" \
-		    -nodes -keyout ${!ssl_key} -out ${!ssl_crt}
-		check
-		chmod 644 ${!ssl_crt}
-	    fi
 	done
 
 	# Install HTTPS certificates into both /etc/pki (Fedora Core