From 1c3849eb576dc9d4cd3d4a39aff9da78be0ddcba Mon Sep 17 00:00:00 2001 From: Karl MacMillan Date: Wed, 12 Dec 2007 11:19:42 -0500 Subject: User provided certs. --- ipa-server/ipa-install/Makefile.am | 1 + ipa-server/ipa-install/ipa-server-certinstall | 156 ++++++++++++++++++++++++++ ipa-server/ipaserver/certs.py | 49 +++++++- ipa-server/ipaserver/dsinstance.py | 2 +- ipa-server/ipaserver/httpinstance.py | 18 +-- ipa-server/ipaserver/installutils.py | 19 ++++ 6 files changed, 224 insertions(+), 21 deletions(-) create mode 100644 ipa-server/ipa-install/ipa-server-certinstall (limited to 'ipa-server') diff --git a/ipa-server/ipa-install/Makefile.am b/ipa-server/ipa-install/Makefile.am index b9ce69156..37dd325e8 100644 --- a/ipa-server/ipa-install/Makefile.am +++ b/ipa-server/ipa-install/Makefile.am @@ -8,6 +8,7 @@ sbin_SCRIPTS = \ ipa-server-install \ ipa-replica-install \ ipa-replica-prepare \ + ipa-server-certinstall \ ipa-radius-install \ $(NULL) diff --git a/ipa-server/ipa-install/ipa-server-certinstall b/ipa-server/ipa-install/ipa-server-certinstall new file mode 100644 index 000000000..932a6be18 --- /dev/null +++ b/ipa-server/ipa-install/ipa-server-certinstall @@ -0,0 +1,156 @@ +#! /usr/bin/python -E +# Authors: Karl MacMillan +# +# 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 sys +sys.path.append("/usr/share/ipa") + +import traceback + +import krbV, ldap, getpass + +from ipaserver import certs, dsinstance, httpinstance, ipaldap, installutils + +def get_realm_name(): + c = krbV.default_context() + return c.default_realm + +def parse_options(): + from optparse import OptionParser + parser = OptionParser() + + parser.add_option("-d", "--dirsrv", dest="dirsrv", action="store_true", + default=False, help="install certificate for the directory server") + parser.add_option("-w", "--http", dest="http", action="store_true", + default=False, help="install certificate for the http server") + + + options, args = parser.parse_args() + + if not options.dirsrv and not options.http: + parser.error("you must specify dirsrv and/or http") + + if len(args) != 1: + parser.error("you must provide a pkcs12 filename") + + return options, args[0] + +def set_ds_cert_name(cert_name, dm_password): + conn = ipaldap.IPAdmin("127.0.0.1") + conn.simple_bind_s("cn=directory manager", dm_password) + + mod = [(ldap.MOD_REPLACE, "nsSSLPersonalitySSL", cert_name)] + + conn.modify_s("cn=RSA,cn=encryption,cn=config", mod) + + conn.unbind() + +def set_http_cert_name(cert_name): + # find the existing cert name + fd = open(httpinstance.NSS_CONF) + nick_name = None + file = [] + for line in fd: + if "NSSNickname" in line: + file.append('NSSNickname "%s"\n' % cert_name) + else: + file.append(line) + fd.close() + + fd = open(httpinstance.NSS_CONF, "w") + fd.write("".join(file)) + fd.close() + + +def choose_server_cert(server_certs): + print "Please select the certificate to use:" + num = 1 + for cert in server_certs: + print "%d. %s" % (num, cert[0]) + num += 1 + + cert_num = 0 + while 1: + cert_input = raw_input("Certificate number [1]: ") + print "" + if cert_input == "": + break + else: + try: + num = int(cert_input) + except ValueError: + print "invalid number" + continue + if num > len(server_certs): + print "number out of range" + continue + cert_num = num - 1 + break + return server_certs[cert_num] + + +def import_cert(dirname, pkcs12_fname): + cdb = certs.CertDB(dirname) + cdb.create_passwd_file(False) + cdb.create_certdbs() + try: + cdb.import_pkcs12(pkcs12_fname) + except RuntimeError, e: + print str(e) + sys.exit(1) + + server_certs = cdb.find_server_certs() + if len(server_certs) == 0: + print "could not find a suitable server cert in import" + sys.exit(1) + elif len(server_certs) == 1: + server_cert = server_certs[0] + else: + server_cert = choose_server_cert(server_certs) + + cdb.trust_root_cert(server_cert[0]) + + return server_cert + +def main(): + options, pkcs12_fname = parse_options() + + try: + if options.dirsrv: + dm_password = getpass.getpass("Directory Manager password: ") + realm = get_realm_name() + dirname = dsinstance.config_dirname(realm) + server_cert = import_cert(dirname, pkcs12_fname) + set_ds_cert_name(server_cert[0], dm_password) + + if options.http: + dirname = httpinstance.NSS_DIR + server_cert = import_cert(dirname, pkcs12_fname) + print server_cert + set_http_cert_name(server_cert[0]) + + except Exception, e: + print "an unexpected error occurred: %s" % str(e) + traceback.print_exc() + return 1 + + return 0 + + +sys.exit(main()) diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py index fb6b01d0e..77052c13e 100644 --- a/ipa-server/ipaserver/certs.py +++ b/ipa-server/ipaserver/certs.py @@ -17,7 +17,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -import os, stat, subprocess +import os, stat, subprocess, re import sha from ipa import ipautil @@ -196,6 +196,50 @@ class CertDB(object): f.close() self.set_perms(self.pin_fname) + def trust_root_cert(self, nickname): + p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, + "-O", "-n", nickname], stdout=subprocess.PIPE) + + chain = p.stdout.read() + chain = chain.split("\n") + + root_nickname = re.match('\ *"(.*)".*', chain[0]).groups()[0] + + self.run_certutil(["-M", "-n", root_nickname, + "-t", "CT,CT,"]) + + def find_server_certs(self): + p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, + "-L"], stdout=subprocess.PIPE) + + certs = p.stdout.read() + + certs = certs.split("\n") + + server_certs = [] + + for cert in certs: + fields = cert.split() + if not len(fields): + continue + flags = fields[-1] + if 'u' in flags: + name = " ".join(fields[0:-1]) + server_certs.append((name, flags)) + + return server_certs + + + def import_pkcs12(self, pkcs12_fname): + try: + ipautil.run(["/usr/bin/pk12util", "-d", self.secdir, + "-i", pkcs12_fname]) + except ipautil.CalledProcessError, e: + if e.returncode == 17: + raise RuntimeError("incorrect password") + else: + raise RuntimeError("unknown error import pkcs#12 file") + def create_self_signed(self, passwd=True): self.create_noise_file() self.create_passwd_file(passwd) @@ -208,6 +252,3 @@ class CertDB(object): self.create_passwd_file(passwd) self.create_certdbs() self.load_cacert(cacert_fname) - - - diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py index 5edc3879e..818710fb6 100644 --- a/ipa-server/ipaserver/dsinstance.py +++ b/ipa-server/ipaserver/dsinstance.py @@ -322,7 +322,7 @@ class DsInstance(service.Service): conn.addEntry(entry) conn.unbind() - + def __add_default_layout(self): self.step("adding default layout") txt = ipautil.template_file(ipautil.SHARE_DIR + "bootstrap-template.ldif", self.sub_dict) diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py index 1799cca07..de0b3af82 100644 --- a/ipa-server/ipaserver/httpinstance.py +++ b/ipa-server/ipaserver/httpinstance.py @@ -29,6 +29,7 @@ import time import service import certs import dsinstance +import installutils from ipa.ipautil import * HTTPD_DIR = "/etc/httpd" @@ -43,21 +44,6 @@ successfully change with the command: Try updating the policycoreutils and selinux-policy packages. """ -def update_file(filename, orig, subst): - if os.path.exists(filename): - pattern = "%s" % re.escape(orig) - p = re.compile(pattern) - for line in fileinput.input(filename, inplace=1): - if not p.search(line): - sys.stdout.write(line) - else: - sys.stdout.write(p.sub(subst, line)) - fileinput.close() - return 0 - else: - print "File %s doesn't exist." % filename - return 1 - class HTTPInstance(service.Service): def __init__(self): service.Service.__init__(self, "httpd") @@ -145,7 +131,7 @@ class HTTPInstance(service.Service): def __set_mod_nss_port(self): self.step("Setting mod_nss port to 443") - if update_file(NSS_CONF, '8443', '443') != 0: + if installutils.update_file(NSS_CONF, '8443', '443') != 0: print "Updating %s failed." % NSS_CONF def __setup_ssl(self): diff --git a/ipa-server/ipaserver/installutils.py b/ipa-server/ipaserver/installutils.py index a403e8154..25cd1555c 100644 --- a/ipa-server/ipaserver/installutils.py +++ b/ipa-server/ipaserver/installutils.py @@ -21,6 +21,10 @@ import logging import socket import errno import getpass +import os +import re +import fileinput +import sys def get_fqdn(): fqdn = "" @@ -105,4 +109,19 @@ def read_password(user): print "" return pwd +def update_file(filename, orig, subst): + if os.path.exists(filename): + pattern = "%s" % re.escape(orig) + p = re.compile(pattern) + for line in fileinput.input(filename, inplace=1): + if not p.search(line): + sys.stdout.write(line) + else: + sys.stdout.write(p.sub(subst, line)) + fileinput.close() + return 0 + else: + print "File %s doesn't exist." % filename + return 1 + -- cgit