diff options
-rw-r--r-- | install/share/Makefile.am | 1 | ||||
-rw-r--r-- | install/share/caJarSigningCert.cfg.template | 88 | ||||
-rw-r--r-- | ipaserver/install/cainstance.py | 40 | ||||
-rw-r--r-- | ipaserver/install/certs.py | 149 | ||||
-rw-r--r-- | ipaserver/install/httpinstance.py | 9 | ||||
-rw-r--r-- | ipaserver/install/installutils.py | 29 |
6 files changed, 259 insertions, 57 deletions
diff --git a/install/share/Makefile.am b/install/share/Makefile.am index 6ef43ba24..3a2ef87d5 100644 --- a/install/share/Makefile.am +++ b/install/share/Makefile.am @@ -10,6 +10,7 @@ app_DATA = \ 60basev2.ldif \ 60policyv2.ldif \ bootstrap-template.ldif \ + caJarSigningCert.cfg.template \ default-aci.ldif \ default-keytypes.ldif \ kerberos.ldif \ diff --git a/install/share/caJarSigningCert.cfg.template b/install/share/caJarSigningCert.cfg.template new file mode 100644 index 000000000..9f018553a --- /dev/null +++ b/install/share/caJarSigningCert.cfg.template @@ -0,0 +1,88 @@ +desc=Jar Signing certificate to auto-configure Firefox +enable=true +enableBy=admin +lastModified=1239836280692 +name=Manual Jar Signing Certificate Enrollment +visible=true +auth.class_id= +auth.instance_id=raCertAuth +input.list=i1,i2 +input.i1.class_id=certReqInputImpl +input.i2.class_id=submitterInfoInputImpl +output.list=o1 +output.o1.class_id=certOutputImpl +policyset.list=caJarSigningSet +policyset.caJarSigningSet.list=1,2,3,6,7,9 +policyset.caJarSigningSet.1.constraint.class_id=subjectNameConstraintImpl +policyset.caJarSigningSet.1.constraint.name=Subject Name Constraint +policyset.caJarSigningSet.1.constraint.params.accept=true +policyset.caJarSigningSet.1.constraint.params.pattern=.* +policyset.caJarSigningSet.1.default.class_id=userSubjectNameDefaultImpl +policyset.caJarSigningSet.1.default.name=Subject Name Default +policyset.caJarSigningSet.1.default.params.name= +policyset.caJarSigningSet.2.constraint.class_id=validityConstraintImpl +policyset.caJarSigningSet.2.constraint.name=Validity Constraint +policyset.caJarSigningSet.2.constraint.params.notAfterCheck=false +policyset.caJarSigningSet.2.constraint.params.notBeforeCheck=false +policyset.caJarSigningSet.2.constraint.params.range=2922 +policyset.caJarSigningSet.2.default.class_id=validityDefaultImpl +policyset.caJarSigningSet.2.default.name=Validity Default +policyset.caJarSigningSet.2.default.params.range=1461 +policyset.caJarSigningSet.2.default.params.startTime=60 +policyset.caJarSigningSet.3.constraint.class_id=keyConstraintImpl +policyset.caJarSigningSet.3.constraint.name=Key Constraint +policyset.caJarSigningSet.3.constraint.params.keyMaxLength=4096 +policyset.caJarSigningSet.3.constraint.params.keyMinLength=1024 +policyset.caJarSigningSet.3.constraint.params.keyType=- +policyset.caJarSigningSet.3.default.class_id=userKeyDefaultImpl +policyset.caJarSigningSet.3.default.name=Key Default +policyset.caJarSigningSet.6.constraint.class_id=keyUsageExtConstraintImpl +policyset.caJarSigningSet.6.constraint.name=Key Usage Extension Constraint +policyset.caJarSigningSet.6.constraint.params.keyUsageCritical=- +policyset.caJarSigningSet.6.constraint.params.keyUsageCrlSign=- +policyset.caJarSigningSet.6.constraint.params.keyUsageDataEncipherment=- +policyset.caJarSigningSet.6.constraint.params.keyUsageDecipherOnly=- +policyset.caJarSigningSet.6.constraint.params.keyUsageDigitalSignature=- +policyset.caJarSigningSet.6.constraint.params.keyUsageEncipherOnly=- +policyset.caJarSigningSet.6.constraint.params.keyUsageKeyAgreement=- +policyset.caJarSigningSet.6.constraint.params.keyUsageKeyCertSign=- +policyset.caJarSigningSet.6.constraint.params.keyUsageKeyEncipherment=- +policyset.caJarSigningSet.6.constraint.params.keyUsageNonRepudiation=- +policyset.caJarSigningSet.6.default.class_id=keyUsageExtDefaultImpl +policyset.caJarSigningSet.6.default.name=Key Usage Default +policyset.caJarSigningSet.6.default.params.keyUsageCritical=true +policyset.caJarSigningSet.6.default.params.keyUsageCrlSign=false +policyset.caJarSigningSet.6.default.params.keyUsageDataEncipherment=false +policyset.caJarSigningSet.6.default.params.keyUsageDecipherOnly=false +policyset.caJarSigningSet.6.default.params.keyUsageDigitalSignature=true +policyset.caJarSigningSet.6.default.params.keyUsageEncipherOnly=false +policyset.caJarSigningSet.6.default.params.keyUsageKeyAgreement=false +policyset.caJarSigningSet.6.default.params.keyUsageKeyCertSign=true +policyset.caJarSigningSet.6.default.params.keyUsageKeyEncipherment=false +policyset.caJarSigningSet.6.default.params.keyUsageNonRepudiation=false +policyset.caJarSigningSet.7.constraint.class_id=nsCertTypeExtConstraintImpl +policyset.caJarSigningSet.7.constraint.name=Netscape Certificate Type Extension Constraint +policyset.caJarSigningSet.7.constraint.params.nsCertCritical=- +policyset.caJarSigningSet.7.constraint.params.nsCertEmail=- +policyset.caJarSigningSet.7.constraint.params.nsCertEmailCA=- +policyset.caJarSigningSet.7.constraint.params.nsCertObjectSigning=- +policyset.caJarSigningSet.7.constraint.params.nsCertObjectSigningCA=- +policyset.caJarSigningSet.7.constraint.params.nsCertSSLCA=- +policyset.caJarSigningSet.7.constraint.params.nsCertSSLClient=- +policyset.caJarSigningSet.7.constraint.params.nsCertSSLServer=- +policyset.caJarSigningSet.7.default.class_id=nsCertTypeExtDefaultImpl +policyset.caJarSigningSet.7.default.name=Netscape Certificate Type Extension Default +policyset.caJarSigningSet.7.default.params.nsCertCritical=false +policyset.caJarSigningSet.7.default.params.nsCertEmail=false +policyset.caJarSigningSet.7.default.params.nsCertEmailCA=false +policyset.caJarSigningSet.7.default.params.nsCertObjectSigning=true +policyset.caJarSigningSet.7.default.params.nsCertObjectSigningCA=false +policyset.caJarSigningSet.7.default.params.nsCertSSLCA=false +policyset.caJarSigningSet.7.default.params.nsCertSSLClient=false +policyset.caJarSigningSet.7.default.params.nsCertSSLServer=false +policyset.caJarSigningSet.9.constraint.class_id=signingAlgConstraintImpl +policyset.caJarSigningSet.9.constraint.name=No Constraint +policyset.caJarSigningSet.9.constraint.params.signingAlgsAllowed=MD5withRSA,MD2withRSA,SHA1withRSA,SHA256withRSA,SHA512withRSA,SHA1withDSA,SHA1withEC +policyset.caJarSigningSet.9.default.class_id=signingAlgDefaultImpl +policyset.caJarSigningSet.9.default.name=Signing Alg +policyset.caJarSigningSet.9.default.params.signingAlg=- diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 088744a48..d33901dc1 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -34,6 +34,7 @@ import shutil import httplib import urllib import xml.dom.minidom +import stat from nss.error import NSPRError import nss.nss as nss @@ -44,7 +45,6 @@ from ipapython import nsslib from ipaserver.install import service from ipaserver.install import installutils from ipaserver import ipaldap -from ipaserver.install import ldapupdate from ipaserver.install import dsinstance from ipalib import util @@ -368,6 +368,7 @@ class CAInstance(service.Service): self.server_root = "/var/lib" self.secure_port = 9444 self.ra_cert = None + self.requestId = None def __del__(self): shutil.rmtree(self.ca_agent_db, ignore_errors=True) @@ -390,6 +391,7 @@ class CAInstance(service.Service): self.step("issuing RA agent certificate", self.__issue_ra_cert) self.step("adding RA agent as a trusted user", self.__configure_ra) self.step("fixing RA database permissions", self.__fix_ra_perms) + self.step("setting up signing cert profile", self.__setup_sign_profile) self.step("configuring certificate server to start on boot", self.__enable) self.step("restarting certificate server", self.__restart_instance) @@ -540,7 +542,7 @@ class CAInstance(service.Service): def __restart_instance(self): try: self.restart() - except Exception: + except Exception, e: # TODO: roll back here? logging.critical("Failed to restart the certificate server. See the installation log for details.") @@ -577,9 +579,10 @@ class CAInstance(service.Service): '-n', 'ipa-ca-agent', '-p', self.admin_password, '-d', self.ca_agent_db, - '-r', '/ca/agent/ca/profileReview?requestId=7', + '-r', '/ca/agent/ca/profileReview?requestId=%s' % self.requestId, '%s:%d' % (self.host_name, 9443), ] + logging.debug("running sslget %s" % args) (stdout, stderr) = ipautil.run(args) data = stdout.split('\r\n') @@ -589,6 +592,8 @@ class CAInstance(service.Service): params['submit'] = 'submit' params['requestNotes'] = '' params = urllib.urlencode(params) + headers = {"Content-type": "application/x-www-form-urlencoded", + "Accept": "text/plain"} # Now issue the RA certificate. args = [ @@ -600,6 +605,7 @@ class CAInstance(service.Service): '-r', '/ca/agent/ca/profileProcess', '%s:%d' % (self.host_name, 9443), ] + logging.debug("running sslget %s" % args) (stdout, stderr) = ipautil.run(args) data = stdout.split('\r\n') @@ -679,6 +685,7 @@ class CAInstance(service.Service): f = os.open(self.ra_agent_pwd, os.O_CREAT | os.O_RDWR) os.write(f, hex_str) os.close(f) + os.chmod(self.ra_agent_pwd, stat.S_IRUSR) stdout, stderr = self.__run_certutil(["-N"]) @@ -758,11 +765,14 @@ class CAInstance(service.Service): res = conn.getresponse() if res.status == 200: data = res.read() - # FIXME: pull the requestId out so of the response so it isn't - # later hard-coded at 7 -# print data - conn.close() + doc = xml.dom.minidom.parseString(data) + item_node = doc.getElementsByTagName("RequestId") + self.requestId = item_node[0].childNodes[0].data + doc.unlink() + self.requestId = self.requestId.strip() + if self.requestId is None: + raise RuntimeError("Unable to determine RA certificate requestId") else: conn.close() raise RuntimeError("Unable to submit RA cert request") @@ -778,6 +788,22 @@ class CAInstance(service.Service): os.chown(self.ra_agent_db + "/secmod.db", 0, pent.pw_gid ) os.chown(self.ra_agent_pwd, 0, pent.pw_gid) + def __setup_sign_profile(self): + caconfig = "/var/lib/pki-ca/conf/CS.cfg" + + if not ipautil.file_exists('/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg'): + profile = ipautil.template_file(ipautil.SHARE_DIR + "caJarSigningCert.cfg.template", {}) + fd = open("/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg", "w") + fd.write(profile) + fd.close() + + profilelist = installutils.get_directive(caconfig, "profile.list", separator="=") + if profilelist.find('caJarSigningCert') < 0: + profilelist = profilelist + ',caJarSigningCert' + installutils.set_directive(caconfig, 'profile.list', profilelist, quotes=False, separator='=') + installutils.set_directive(caconfig, 'profile.caJarSigningCert.class_id', 'caEnrollImpl', quotes=False, separator='=') + installutils.set_directive(caconfig, 'profile.caJarSigningCert.config', '/var/lib/pki-ca/profiles/ca/caJarSigningCert.cfg', quotes=False, separator='=') + def uninstall(self): try: ipautil.run(["/usr/bin/pkiremove", "-pki_instance_root=/var/lib", diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index 45958f2ae..bad9a1e61 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -23,8 +23,10 @@ import errno import tempfile import shutil import logging +import httplib import urllib import xml.dom.minidom +import pwd from ipapython import nsslib from ipapython import sysrestore @@ -165,8 +167,12 @@ class CertDB(object): return str(self.cur_serial) - def set_perms(self, fname, write=False): - os.chown(fname, self.uid, self.gid) + def set_perms(self, fname, write=False, uid=None): + if uid: + pent = pwd.getpwnam(uid) + os.chown(fname, pent.pw_uid, pent.pw_gid) + else: + os.chown(fname, self.uid, self.gid) perms = stat.S_IRUSR if write: perms |= stat.S_IWUSR @@ -181,7 +187,13 @@ class CertDB(object): return ipautil.run(new_args, stdin) def run_signtool(self, args, stdin=None): - new_args = ["/usr/bin/signtool", "-d", self.secdir] + if not self.self_signed_ca: + f = open(self.passwd_fname, "r") + password = f.readline() + f.close() + new_args = ["/usr/bin/signtool", "-d", self.secdir, "-p", password] + else: + new_args = ["/usr/bin/signtool", "-d", self.secdir] new_args = new_args + args ipautil.run(new_args, stdin) @@ -233,7 +245,7 @@ class CertDB(object): self.run_certutil(["-L", "-n", nickname, "-a", "-o", self.cacert_fname]) - self.set_perms(self.cacert_fname) + os.chmod(self.cacert_fname, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH) if create_pkcs12: ipautil.backup_file(self.pk12_fname) ipautil.run(["/usr/bin/pk12util", "-d", self.secdir, @@ -286,11 +298,11 @@ class CertDB(object): os.unlink(self.certreq_fname) os.unlink(self.certder_fname) - def create_signing_cert(self, nickname, name, other_certdb=None): + def create_signing_cert(self, nickname, subject, other_certdb=None): cdb = other_certdb if not cdb: cdb = self - self.request_cert(name) + self.request_cert(subject) cdb.issue_signing_cert(self.certreq_fname, self.certder_fname) self.add_cert(self.certder_fname, nickname) os.unlink(self.certreq_fname) @@ -365,8 +377,8 @@ class CertDB(object): headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} - # Send the CSR request to the CA - f = open(self.passwd_fname) + # Send the request to the CA + f = open(self.passwd_fname, "r") password = f.readline() f.close() conn = nsslib.NSSConnection(self.host_name, 9444, dbdir=self.secdir) @@ -385,6 +397,7 @@ class CertDB(object): item_node = doc.getElementsByTagName("b64") cert = item_node[0].childNodes[0].data doc.unlink() + conn.close() # Write the certificate to a file. It will be imported in a later # step. @@ -395,34 +408,87 @@ class CertDB(object): return def issue_signing_cert(self, certreq_fname, cert_fname): - p = subprocess.Popen(["/usr/bin/certutil", - "-d", self.secdir, - "-C", "-c", self.cacert_name, - "-i", certreq_fname, - "-o", cert_fname, - "-m", self.next_serial(), - "-v", self.valid_months, - "-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. - # - # What this is requesting is: - # -1 (Create key usage extension) - # 0 - Digital Signature - # 5 - Cert signing key - # 9 - done - # n - not critical - # - # -5 (Create netscape cert type extension) - # 3 - Object Signing - # 9 - done - # n - not critical - p.stdin.write("0\n5\n9\nn\n3\n9\nn\n") - p.wait() + if self.self_signed_ca: + p = subprocess.Popen(["/usr/bin/certutil", + "-d", self.secdir, + "-C", "-c", self.cacert_name, + "-i", certreq_fname, + "-o", cert_fname, + "-m", self.next_serial(), + "-v", self.valid_months, + "-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. + # + # What this is requesting is: + # -1 (Create key usage extension) + # 0 - Digital Signature + # 5 - Cert signing key + # 9 - done + # n - not critical + # + # -5 (Create netscape cert type extension) + # 3 - Object Signing + # 9 - done + # n - not critical + p.stdin.write("0\n5\n9\nn\n3\n9\nn\n") + p.wait() + else: + if self.host_name is None: + raise RuntimeError("CA Host is not set.") + + f = open(certreq_fname, "r") + csr = f.readlines() + f.close() + csr = "".join(csr) + + # We just want the CSR bits, make sure there is no thing else + s = csr.find("-----BEGIN NEW CERTIFICATE REQUEST-----") + e = csr.find("-----END NEW CERTIFICATE REQUEST-----") + if e > 0: + e = e + 37 + if s >= 0: + csr = csr[s:] + + params = urllib.urlencode({'profileId': 'caJarSigningCert', + 'cert_request_type': 'pkcs10', + 'requestor_name': 'IPA Installer', + 'cert_request': csr, + 'xmlOutput': 'true'}) + headers = {"Content-type": "application/x-www-form-urlencoded", + "Accept": "text/plain"} + + # Send the request to the CA + f = open(self.passwd_fname, "r") + password = f.readline() + f.close() + conn = nsslib.NSSConnection(self.host_name, 9444, dbdir=self.secdir) + conn.sslsock.set_client_auth_data_callback(client_auth_data_callback, "ipaCert", password, nss.get_default_certdb()) + conn.set_debuglevel(0) + + conn.request("POST", "/ca/ee/ca/profileSubmit", params, headers) + res = conn.getresponse() + data = res.read() + conn.close() + if res.status != 200: + raise RuntimeError("Unable to submit cert request") + + # The result is an XML blob. Pull the certificate out of that + doc = xml.dom.minidom.parseString(data) + item_node = doc.getElementsByTagName("b64") + cert = item_node[0].childNodes[0].data + doc.unlink() + conn.close() + + f = open(cert_fname, "w") + f.write(cert) + f.close() + + return def add_cert(self, cert_fname, nickname): args = ["-A", "-n", nickname, @@ -440,9 +506,10 @@ class CertDB(object): 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()) + pwdfile = open(self.passwd_fname) + f.write(pwdfile.read()) f.close() + pwdfile.close() self.set_perms(self.pin_fname) def create_password_conf(self): @@ -452,9 +519,11 @@ class CertDB(object): ipautil.backup_file(self.pwd_conf) f = open(self.pwd_conf, "w") f.write("internal:") - pwd = open(self.passwd_fname) - f.write(pwd.read()) + pwdfile = open(self.passwd_fname) + f.write(pwdfile.read()) f.close() + pwdfile.close() + self.set_perms(self.pwd_conf, uid="apache") def find_root_cert(self, nickname): p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir, diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index 0637e3f45..1bd9ffd5c 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -183,6 +183,7 @@ class HTTPInstance(service.Service): db.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ca_db) else: db.create_server_cert("Server-Cert", "CN=%s,OU=ipa-pki,O=IPA" % self.fqdn, ca_db) + db.create_signing_cert("Signing-Cert", "CN=Object Signing Cert,OU=ipa-pki,O=IPA", ca_db) db.create_password_conf() # Fix the database permissions @@ -196,20 +197,12 @@ class HTTPInstance(service.Service): os.chown(NSS_DIR + "/secmod.db", 0, pent.pw_gid ) def __setup_autoconfig(self): - # FIXME. Need to issue the self-signed cert from the CA as well. - # A special profile is needed from the CS team to do this. - if not self.self_signed_ca: - return prefs_txt = ipautil.template_file(ipautil.SHARE_DIR + "preferences.html.template", self.sub_dict) prefs_fd = open("/usr/share/ipa/html/preferences.html", "w") prefs_fd.write(prefs_txt) prefs_fd.close() # The signing cert is generated in __setup_ssl - if self.self_signed_ca: - ca_db = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm))) - else: - ca_db = certs.CertDB(NSS_DIR) db = certs.CertDB(NSS_DIR) tmpdir = tempfile.mkdtemp(prefix = "tmp-") diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index f5e04636c..c12023719 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -210,24 +210,49 @@ def update_file(filename, orig, subst): print "File %s doesn't exist." % filename return 1 -def set_directive(filename, directive, value): +def set_directive(filename, directive, value, quotes=True, separator=' '): """Set a name/value pair directive in a configuration file. This has only been tested with nss.conf """ + valueset = False fd = open(filename) file = [] for line in fd: if directive in line: - file.append('%s "%s"\n' % (directive, value)) + valueset = True + if quotes: + file.append('%s%s"%s"\n' % (directive, separator, value)) + else: + file.append('%s%s%s\n' % (directive, separator, value)) else: file.append(line) fd.close() + if not valueset: + if quotes: + file.append('%s%s"%s"\n' % (directive, separator, value)) + else: + file.append('%s%s%s\n' % (directive, separator, value)) fd = open(filename, "w") fd.write("".join(file)) fd.close() +def get_directive(filename, directive, strip_quotes=True, separator=' '): + """ + A rather inefficient way to get a configuration directive. + """ + fd = open(filename, "r") + for line in fd: + if directive in line: + line = line.strip() + result = line.split(separator, 1)[1] + result = result.strip('"') + fd.close() + return result + fd.close() + return None + def kadmin(command): ipautil.run(["/usr/kerberos/sbin/kadmin.local", "-q", command]) |