Convert the setup of ssl from a shell script to a

python module. This is in preparation for user
supplied certs.
This commit is contained in:
Karl MacMillan
-
parent ad3fcc200c
commit 3b4f0db73e
7 changed files with 245 additions and 229 deletions

View File

@@ -27,6 +27,7 @@ from random import Random
from time import gmtime from time import gmtime
import os, sys, traceback, readline import os, sys, traceback, readline
import stat import stat
import shutil
from types import * from types import *
@@ -101,6 +102,15 @@ def dir_exists(filename):
except: except:
return False return False
def install_file(fname, dest):
if file_exists(dest):
os.rename(dest, dest + ".orig")
shutil.move(fname, dest)
def backup_file(fname):
if file_exists(fname):
os.rename(fname, fname + ".orig")
class CIDict(dict): class CIDict(dict):
""" """
Case-insensitive but case-respecting dictionary. Case-insensitive but case-respecting dictionary.

View File

@@ -10,14 +10,8 @@ sbin_SCRIPTS = \
ipa-replica-prepare \ ipa-replica-prepare \
$(NULL) $(NULL)
appdir = $(IPA_DATA_DIR)
app_SCRIPTS = \
ipa-server-setupssl \
$(NULL)
EXTRA_DIST = \ EXTRA_DIST = \
README \ README \
$(app_SCRIPTS) \
$(sbin_SCRIPTS) \ $(sbin_SCRIPTS) \
$(NULL) $(NULL)

View File

@@ -1,216 +0,0 @@
#!/bin/bash
if [ "$1" ] ; then
password=$1
else
echo "password required"
exit 1
fi
if [ "$2" -a -d "$2" ] ; then
secdir="$2"
else
secdir=/etc/dirsrv/slapd-localhost
fi
if [ "$3" ] ; then
myhost=$3
else
myhost=`hostname --fqdn`
fi
if [ "$4" ] ; then
ldapport=$4
else
ldapport=389
fi
me=`whoami`
if [ "$me" = "root" ] ; then
isroot=1
fi
# see if there are already certs and keys
if [ -f $secdir/cert8.db ] ; then
# look for CA cert
if certutil -L -d $secdir -n "CA certificate" 2> /dev/null ; then
echo "Using existing CA certificate"
else
echo "No CA certificate found - will create new one"
needCA=1
fi
# look for server cert
if certutil -L -d $secdir -n "Server-Cert" 2> /dev/null ; then
echo "Using existing directory Server-Cert"
else
echo "No Server Cert found - will create new one"
needServerCert=1
fi
prefix="new-"
prefixarg="-P $prefix"
else
needCA=1
needServerCert=1
fi
if test -z "$needCA" -a -z "$needServerCert" ; then
echo "No certs needed - exiting"
exit 0
fi
# get our user and group
if test -n "$isroot" ; then
uid=`/bin/ls -ald $secdir | awk '{print $3}'`
gid=`/bin/ls -ald $secdir | awk '{print $4}'`
fi
# 2. Create a password file for your security token password:
if [ -f $secdir/pwdfile.txt ] ; then
echo "Using existing $secdir/pwdfile.txt"
else
(ps -ef ; w ) | sha1sum | awk '{print $1}' > $secdir/pwdfile.txt
if test -n "$isroot" ; then
chown $uid:$gid $secdir/pwdfile.txt
fi
chmod 400 $secdir/pwdfile.txt
fi
# 3. Create a "noise" file for your encryption mechanism:
if [ -f $secdir/noise.txt ] ; then
echo "Using existing $secdir/noise.txt file"
else
(w ; ps -ef ; date ) | sha1sum | awk '{print $1}' > $secdir/noise.txt
if test -n "$isroot" ; then
chown $uid:$gid $secdir/noise.txt
fi
chmod 400 $secdir/noise.txt
fi
# 4. Create the key3.db and cert8.db databases:
certutil -N $prefixarg -d $secdir -f $secdir/pwdfile.txt
if test -n "$isroot" ; then
chown $uid:$gid $secdir/${prefix}key3.db $secdir/${prefix}cert8.db
fi
chmod 600 $secdir/${prefix}key3.db $secdir/${prefix}cert8.db
if test -n "$needCA" ; then
# 5. Generate the encryption key:
certutil -G $prefixarg -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
# 6. Generate the self-signed certificate:
certutil -S $prefixarg -n "CA certificate" -s "cn=CAcert" -x -t "CT,," -m 1000 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
# export the CA cert for use with other apps
certutil -L $prefixarg -d $secdir -n "CA certificate" -a > $secdir/cacert.asc
pk12util -d $secdir $prefixarg -o $secdir/cacert.p12 -n "CA certificate" -w $secdir/pwdfile.txt -k $secdir/pwdfile.txt
fi
if test -n "$needServerCert" ; then
# 7. Generate the server certificate:
certutil -S $prefixarg -n "Server-Cert" -s "cn=$myhost,ou=Fedora Directory Server" -c "CA certificate" -t "u,u,u" -m 1001 -v 120 -d $secdir -z $secdir/noise.txt -f $secdir/pwdfile.txt
fi
# create the pin file
if [ ! -f $secdir/pin.txt ] ; then
pinfile=$secdir/pin.txt
echo 'Internal (Software) Token:'`cat $secdir/pwdfile.txt` > $pinfile
if test -n "$isroot" ; then
chown $uid:$gid $pinfile
fi
chmod 400 $pinfile
else
echo Using existing $secdir/pin.txt
fi
if [ -n "$prefix" ] ; then
# move the old files out of the way
mv $secdir/cert8.db $secdir/orig-cert8.db
mv $secdir/key3.db $secdir/orig-key3.db
# move in the new files - will be used after server restart
mv $secdir/${prefix}cert8.db $secdir/cert8.db
mv $secdir/${prefix}key3.db $secdir/key3.db
fi
modnssdir=/etc/httpd/alias
# Setup SSL in Apache
if [ -e $modnssdir ]; then
mkdir ${modnssdir}.ipa
mv $modnssdir/cert8.db ${modnssdir}.ipa
mv $modnssdir/key3.db ${modnssdir}.ipa
fi
# Create a new database for mod_nss
echo -e "\n" > $modnssdir/pw.txt
certutil -N -d $modnssdir -f $modnssdir/pw.txt
# Add the CA we created
certutil -A -d $modnssdir -n "CA certificate" -t "CT,CT," -a -i $secdir/cacert.asc
# Request a new server cert
certutil -R -d $modnssdir \
-s "cn=$myhost,ou=Apache Web Server" \
-o $modnssdir/tmpcertreq \
-g 1024 \
-z $secdir/noise.txt \
-f $modnssdir/pw.txt
# Have the FDS CA issue the cert
echo -e "2\n9\nn\n1\n9\nn\n" | \
certutil -C -d $secdir \
-c "CA certificate" \
-i $modnssdir/tmpcertreq \
-o $modnssdir/tmpcert.der \
-m 1002 \
-v 120 \
-f $secdir/pwdfile.txt \
-1 \
-5
# Now add this cert to the Apache database
certutil -A -d $modnssdir -n "Server-Cert"\
-t u,u,u \
-i $modnssdir/tmpcert.der \
-f $modnsdir/tmpcert.der
rm -f $modnssdir/pw.txt $modnssdir/tmpcertreq $modnssder/tmpcert.der
# enable SSL in the directory server
ldapmodify -x -h localhost -p $ldapport -D "cn=Directory Manager" -w $password <<EOF
dn: cn=encryption,cn=config
changetype: modify
replace: nsSSL3
nsSSL3: on
-
replace: nsSSLClientAuth
nsSSLClientAuth: allowed
-
add: nsSSL3Ciphers
nsSSL3Ciphers: -rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,
+rsa_des_sha,+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,+fortezza,
+fortezza_rc4_128_sha,+fortezza_null,+tls_rsa_export1024_with_rc4_56_sha,
+tls_rsa_export1024_with_des_cbc_sha
dn: cn=config
changetype: modify
add: nsslapd-security
nsslapd-security: on
-
replace: nsslapd-ssl-check-hostname
nsslapd-ssl-check-hostname: off
dn: cn=RSA,cn=encryption,cn=config
changetype: add
objectclass: top
objectclass: nsEncryptionModule
cn: RSA
nsSSLPersonalitySSL: Server-Cert
nsSSLToken: internal (software)
nsSSLActivation: on
EOF

View File

@@ -14,6 +14,7 @@ app_PYTHON = \
service.py \ service.py \
installutils.py \ installutils.py \
replication.py \ replication.py \
certs.py \
$(NULL) $(NULL)
EXTRA_DIST = \ EXTRA_DIST = \

View File

@@ -0,0 +1,192 @@
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 or later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
import os, stat, subprocess
import sha
from ipa import ipautil
class CertDB(object):
def __init__(self, dir):
self.secdir = dir
self.prefix = "new-"
self.noise_fname = self.secdir + "/noise.txt"
self.passwd_fname = self.secdir + "/pwdfile.txt"
self.certdb_fname = self.secdir + "/cert8.db"
self.keydb_fname = self.secdir + "/key3.db"
self.cacert_fname = self.secdir + "/cacert.asc"
self.pk12_fname = self.secdir + "/cacert.p12"
self.pin_fname = self.secdir + "/pin.txt"
self.certreq_fname = self.secdir + "/tmpcertreq"
self.certder_fname = self.secdir + "/tmpcert.der"
# We are going to set the owner of all of the cert
# files to the owner of the containing directory
# instead of that of the process. This works when
# this is called by root for a daemon that runs as
# a normal user
mode = os.stat(self.secdir)
self.uid = mode[stat.ST_UID]
self.gid = mode[stat.ST_GID]
def set_perms(self, fname, write=False):
os.chown(fname, self.uid, self.gid)
perms = stat.S_IRUSR
if write:
perms |= stat.S_IWUSR
os.chmod(fname, perms)
def gen_password(self):
return sha.sha(ipautil.ipa_generate_password()).hexdigest()
def run_certutil(self, args, stdin=None):
new_args = ["/usr/bin/certutil", "-d", self.secdir]
new_args = new_args + args
ipautil.run(new_args, stdin)
def create_noise_file(self):
ipautil.backup_file(self.noise_fname)
f = open(self.noise_fname, "w")
f.write(self.gen_password())
self.set_perms(self.noise_fname)
def create_passwd_file(self, passwd=True):
ipautil.backup_file(self.passwd_fname)
f = open(self.passwd_fname, "w")
if passwd:
f.write(self.gen_password())
else:
f.write("\n")
f.close()
self.set_perms(self.passwd_fname)
def create_certdbs(self):
ipautil.backup_file(self.certdb_fname)
ipautil.backup_file(self.keydb_fname)
self.run_certutil(["-N",
"-f", self.passwd_fname])
self.set_perms(self.passwd_fname, write=True)
def create_ca_cert(self):
# Generate the encryption key
self.run_certutil(["-G", "-z", self.noise_fname, "-f", self.passwd_fname])
# Generate the self-signed cert
self.run_certutil(["-S", "-n", "CA certificate",
"-s", "cn=CAcert",
"-x",
"-t", "CT,,",
"-m", "1000",
"-v", "120",
"-z", self.noise_fname,
"-f", self.passwd_fname])
# export the CA cert for use with other apps
ipautil.backup_file(self.cacert_fname)
self.run_certutil(["-L", "-n", "CA certificate",
"-a",
"-o", self.cacert_fname])
self.set_perms(self.cacert_fname)
ipautil.backup_file(self.pk12_fname)
ipautil.run(["/usr/bin/pk12util", "-d", self.secdir,
"-o", self.pk12_fname,
"-n", "CA certificate",
"-w", self.passwd_fname,
"-k", self.passwd_fname])
self.set_perms(self.pk12_fname)
def load_cacert(self, cacert_fname):
self.run_certutil(["-A", "-n", "CA certificate",
"-t", "CT,CT,",
"-a",
"-i", cacert_fname])
def create_server_cert(self, nickname, name):
self.run_certutil(["-S", "-n", nickname,
"-s", name,
"-c", "CA certificate",
"-t", "u,u,u",
"-m", "1001",
"-v", "120",
"-z", self.noise_fname,
"-f", self.passwd_fname])
def request_cert(self, name):
self.run_certutil(["-R", "-s", name,
"-o", self.certreq_fname,
"-g", "1024",
"-z", self.noise_fname,
"-f", self.passwd_fname])
def issue_cert(self, certreq_fname, cert_fname):
p = subprocess.Popen(["/usr/bin/certutil",
"-d", self.secdir,
"-C", "-c", "CA certificate",
"-i", certreq_fname,
"-o", cert_fname,
"-m", "1002",
"-v", "120",
"-f", self.passwd_fname,
"-1", "-5"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
# bah - this sucks, but I guess it isn't possible to fully
# control this with command line arguments
p.stdin.write("2\n9\nn\n1\n9\nn\n")
p.wait()
def add_cert(self, cert_fname, nickname):
self.run_certutil(["-A", "-n", nickname,
"-t", "u,u,u",
"-i", cert_fname,
"-f", cert_fname])
def create_server_cert_extca(self, nickname, name, other_certdb):
self.request_cert(name)
other_certdb.issue_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
def create_pin_file(self):
ipautil.backup_file(self.pin_fname)
f = open(self.pin_fname, "w")
f.write("Internal (Software) Token:")
pwd = open(self.passwd_fname)
f.write(pwd.read())
f.close()
self.set_perms(self.pin_fname)
def create_self_signed(self, passwd=True):
self.create_noise_file()
self.create_passwd_file(passwd)
self.create_certdbs()
self.create_ca_cert()
self.create_pin_file()
def create_from_cacert(self, cacert_fname, passwd=False):
self.create_noise_file()
self.create_passwd_file(passwd)
self.create_certdbs()
self.load_cacert(cacert_fname)

View File

@@ -29,6 +29,8 @@ from ipa import ipautil
import service import service
import installutils import installutils
import certs
import ipaldap, ldap
SERVER_ROOT_64 = "/usr/lib64/dirsrv" SERVER_ROOT_64 = "/usr/lib64/dirsrv"
SERVER_ROOT_32 = "/usr/lib/dirsrv" SERVER_ROOT_32 = "/usr/lib/dirsrv"
@@ -290,13 +292,36 @@ class DsInstance(service.Service):
def __enable_ssl(self): def __enable_ssl(self):
self.step("configuring ssl for ds instance") self.step("configuring ssl for ds instance")
dirname = config_dirname(self.realm_name) dirname = config_dirname(self.realm_name)
args = ["/usr/share/ipa/ipa-server-setupssl", self.dm_password, ca = certs.CertDB(dirname)
dirname, self.host_name] ca.create_self_signed()
try: ca.create_server_cert("Server-Cert", "cn=%s,ou=Fedora Directory Server" % self.host_name)
ipautil.run(args)
logging.debug("done configuring ssl for ds instance") conn = ipaldap.IPAdmin("127.0.0.1")
except ipautil.CalledProcessError, e: conn.simple_bind_s("cn=directory manager", self.dm_password)
logging.critical("Failed to configure ssl in ds instance %s" % e)
mod = [(ldap.MOD_REPLACE, "nsSSLClientAuth", "allowed"),
(ldap.MOD_REPLACE, "nsSSL3Ciphers",
"-rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,\
+rsa_des_sha,+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,+fortezza,\
+fortezza_rc4_128_sha,+fortezza_null,+tls_rsa_export1024_with_rc4_56_sha,\
+tls_rsa_export1024_with_des_cbc_sha")]
conn.modify_s("cn=encryption,cn=config", mod)
mod = [(ldap.MOD_ADD, "nsslapd-security", "on"),
(ldap.MOD_REPLACE, "nsslapd-ssl-check-hostname", "off")]
conn.modify_s("cn=config", mod)
entry = ipaldap.Entry("cn=RSA,cn=encryption,cn=config")
entry.setValues("objectclass", "top", "nsEncryptionModule")
entry.setValues("cn", "RSA")
entry.setValues("nsSSLPersonalitySSL", "Server-Cert")
entry.setValues("nsSSLToken", "internal (software)")
entry.setValues("nsSSLActivation", "on")
conn.addEntry(entry)
conn.unbind()
def __add_default_layout(self): def __add_default_layout(self):
self.step("adding default layout") self.step("adding default layout")

View File

@@ -27,6 +27,8 @@ import sys
import time import time
import service import service
import certs
import dsinstance
from ipa.ipautil import * from ipa.ipautil import *
HTTPD_DIR = "/etc/httpd" HTTPD_DIR = "/etc/httpd"
@@ -143,3 +145,11 @@ class HTTPInstance(service.Service):
self.step("Setting mod_nss port to 443") self.step("Setting mod_nss port to 443")
if update_file(NSS_CONF, '8443', '443') != 0: if update_file(NSS_CONF, '8443', '443') != 0:
print "Updating %s failed." % NSS_CONF print "Updating %s failed." % NSS_CONF
def __setup_ssl(self):
self.step("Setting up ssl")
ds_ca = certs.CertDB(dsinstance.config_dirname(self.realm))
ca = certs.CertDB(dirname)
ca.create_from_cacert(ds_ca.cacert_fname)
ca.create_server_cert_extca("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca)