def execute(self, csr, **kw): ca_enabled_check() ldap = self.api.Backend.ldap2 principal = kw.get('principal') add = kw.get('add') request_type = kw.get('request_type') service = None """ Access control is partially handled by the ACI titled 'Hosts can modify service userCertificate'. This is for the case where a machine binds using a host/ prinicpal. It can only do the request if the target hostname is in the managedBy attribute which is managed using the add/del member commands. Binding with a user principal one needs to be in the request_certs taskgroup (directly or indirectly via role membership). """ bind_principal = getattr(context, 'principal') # Can this user request certs? if not bind_principal.startswith('host/'): self.check_access() try: subject = pkcs10.get_subject(csr) extensions = pkcs10.get_extensions(csr) subjectaltname = pkcs10.get_subjectaltname(csr) or () except (NSPRError, PyAsn1Error), e: raise errors.CertificateOperationError( error=_("Failure decoding Certificate Signing Request: %s") % e)
def get_csr_hostname(csr): """ Return the value of CN in the subject of the request or None """ try: request = pkcs10.load_certificate_request(csr) subject = pkcs10.get_subject(request) return subject.common_name except NSPRError, nsprerr: raise errors.CertificateOperationError(error=_('Failure decoding Certificate Signing Request:'))
def test_0(self): """ Test simple CSR with no attributes """ csr = self.read_file("test0.csr") subject = pkcs10.get_subject(csr) assert(subject.common_name == 'test.example.com') assert(subject.state_name == 'California') assert(subject.country_name == 'US')
def get_csr_hostname(csr): """ Return the value of CN in the subject of the request or None """ try: request = pkcs10.load_certificate_request(csr) subject = pkcs10.get_subject(request) return subject.common_name except NSPRError, nsprerr: raise errors.CertificateOperationError( error=_('Failure decoding Certificate Signing Request:'))
def execute(self, csr, **kw): ca_enabled_check() ldap = self.api.Backend.ldap2 add = kw.get('add') request_type = kw.get('request_type') profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE) ca = '.' # top-level CA hardcoded until subca plugin implemented """ Access control is partially handled by the ACI titled 'Hosts can modify service userCertificate'. This is for the case where a machine binds using a host/ prinicpal. It can only do the request if the target hostname is in the managedBy attribute which is managed using the add/del member commands. Binding with a user principal one needs to be in the request_certs taskgroup (directly or indirectly via role membership). """ principal_string = kw.get('principal') principal = split_any_principal(principal_string) servicename, principal_name, realm = principal if servicename is None: principal_type = USER elif servicename == 'host': principal_type = HOST else: principal_type = SERVICE caacl_check(principal_type, principal_string, ca, profile_id) bind_principal = split_any_principal(getattr(context, 'principal')) bind_service, bind_name, bind_realm = bind_principal if bind_service is None: bind_principal_type = USER elif bind_service == 'host': bind_principal_type = HOST else: bind_principal_type = SERVICE if bind_principal != principal and bind_principal_type != HOST: # Can the bound principal request certs for another principal? self.check_access() try: subject = pkcs10.get_subject(csr) extensions = pkcs10.get_extensions(csr) subjectaltname = pkcs10.get_subjectaltname(csr) or () except (NSPRError, PyAsn1Error), e: raise errors.CertificateOperationError( error=_("Failure decoding Certificate Signing Request: %s") % e)
def test_1(self): """ Test CSR with subject alt name """ csr = self.read_file("test1.csr") request = pkcs10.load_certificate_request(csr) subject = pkcs10.get_subject(request) assert(subject.common_name == 'test.example.com') assert(subject.state_name == 'California') assert(subject.country_name == 'US') for extension in request.extensions: if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME: assert nss.x509_alt_name(extension.value)[0] == 'testlow.example.com'
def test_1(self): """ Test CSR with subject alt name """ csr = self.read_file("test1.csr") request = pkcs10.load_certificate_request(csr) subject = pkcs10.get_subject(request) assert (subject.common_name == 'test.example.com') assert (subject.state_name == 'California') assert (subject.country_name == 'US') for extension in request.extensions: if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME: assert nss.x509_alt_name( extension.value)[0] == 'testlow.example.com'
def request_certificate(self, csr, request_type='pkcs10'): """ :param csr: The certificate signing request. :param request_type: The request type (defaults to ``'pkcs10'``). Submit certificate signing request. The command returns a dict with these possible key/value pairs. Some key/value pairs may be absent. +---------------+---------------+---------------+ |result name |result type |comments | +===============+===============+===============+ |serial_number |unicode [1]_ | | +---------------+---------------+---------------+ |certificate |unicode [2]_ | | +---------------+---------------+---------------+ |request_id |unicode | | +---------------+---------------+---------------+ |subject |unicode | | +---------------+---------------+---------------+ .. [1] Passed through XMLRPC as decimal string. Can convert to optimal integer type (int or long) via int(serial_number) .. [2] Base64 encoded """ try: config = api.Command['config_show']()['result'] subject_base = EditableDN(config.get('ipacertificatesubjectbase')[0]) hostname = get_csr_hostname(csr) subject_base.insert(0, RDN(('CN', hostname))) request = pkcs10.load_certificate_request(csr) # python-nss normalizes the request subject request_subject = DN(str(pkcs10.get_subject(request))) if subject_base != request_subject: raise errors.CertificateOperationError(error=_('Request subject "%(request_subject)s" does not match the form "%(subject_base)s"') % \ {'request_subject' : request_subject, 'subject_base' : subject_base}) except errors.CertificateOperationError, e: raise e
def test_2(self): """ Test CSR with subject alt name and a list of CRL distribution points """ csr = self.read_file("test2.csr") request = pkcs10.load_certificate_request(csr) subject = pkcs10.get_subject(request) assert(subject.common_name == 'test.example.com') assert(subject.state_name == 'California') assert(subject.country_name == 'US') for extension in request.extensions: if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME: assert nss.x509_alt_name(extension.value)[0] == 'testlow.example.com' if extension.oid_tag == nss.SEC_OID_X509_CRL_DIST_POINTS: pts = nss.CRLDistributionPts(extension.value) urls = pts[0].get_general_names() assert('http://ca.example.com/my.crl' in urls) assert('http://other.example.com/my.crl' in urls)
def test_2(self): """ Test CSR with subject alt name and a list of CRL distribution points """ csr = self.read_file("test2.csr") request = pkcs10.load_certificate_request(csr) subject = pkcs10.get_subject(request) assert (subject.common_name == 'test.example.com') assert (subject.state_name == 'California') assert (subject.country_name == 'US') for extension in request.extensions: if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME: assert nss.x509_alt_name( extension.value)[0] == 'testlow.example.com' if extension.oid_tag == nss.SEC_OID_X509_CRL_DIST_POINTS: pts = nss.CRLDistributionPts(extension.value) urls = pts[0].get_general_names() assert ('http://ca.example.com/my.crl' in urls) assert ('http://other.example.com/my.crl' in urls)
def execute(self, csr, all=False, raw=False, **kw): ca_enabled_check() ldap = self.api.Backend.ldap2 add = kw.get('add') request_type = kw.get('request_type') profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE) # Check that requested authority exists (done before CA ACL # enforcement so that user gets better error message if # referencing nonexistant CA) and look up authority ID. # ca = kw['cacn'] ca_obj = api.Command.ca_show(ca)['result'] ca_id = ca_obj['ipacaid'][0] """ Access control is partially handled by the ACI titled 'Hosts can modify service userCertificate'. This is for the case where a machine binds using a host/ prinicpal. It can only do the request if the target hostname is in the managedBy attribute which is managed using the add/del member commands. Binding with a user principal one needs to be in the request_certs taskgroup (directly or indirectly via role membership). """ principal = kw.get('principal') principal_string = unicode(principal) if principal.is_user: principal_type = USER elif principal.is_host: principal_type = HOST else: principal_type = SERVICE bind_principal = kerberos.Principal( getattr(context, 'principal')) bind_principal_string = unicode(bind_principal) if bind_principal.is_user: bind_principal_type = USER elif bind_principal.is_host: bind_principal_type = HOST else: bind_principal_type = SERVICE if (bind_principal_string != principal_string and bind_principal_type != HOST): # Can the bound principal request certs for another principal? self.check_access() try: self.check_access("request certificate ignore caacl") bypass_caacl = True except errors.ACIError: bypass_caacl = False if not bypass_caacl: caacl_check(principal_type, principal, ca, profile_id) try: subject = pkcs10.get_subject(csr) extensions = pkcs10.get_extensions(csr) subjectaltname = pkcs10.get_subjectaltname(csr) or () except (NSPRError, PyAsn1Error, ValueError) as e: raise errors.CertificateOperationError( error=_("Failure decoding Certificate Signing Request: %s") % e) # self-service and host principals may bypass SAN permission check if (bind_principal_string != principal_string and bind_principal_type != HOST): if '2.5.29.17' in extensions: self.check_access('request certificate with subjectaltname') dn = None principal_obj = None # See if the service exists and punt if it doesn't and we aren't # going to add it try: if principal_type == SERVICE: principal_obj = api.Command['service_show'](principal_string, all=True) elif principal_type == HOST: principal_obj = api.Command['host_show']( principal.hostname, all=True) elif principal_type == USER: principal_obj = api.Command['user_show']( principal.username, all=True) except errors.NotFound as e: if add: if principal_type == SERVICE: principal_obj = api.Command['service_add']( principal_string, force=True) else: princtype_str = PRINCIPAL_TYPE_STRING_MAP[principal_type] raise errors.OperationNotSupportedForPrincipalType( operation=_("'add' option"), principal_type=princtype_str) else: raise errors.NotFound( reason=_("The principal for this request doesn't exist.")) principal_obj = principal_obj['result'] dn = principal_obj['dn'] # Ensure that the DN in the CSR matches the principal cn = subject.common_name #pylint: disable=E1101 if not cn: raise errors.ValidationError(name='csr', error=_("No Common Name was found in subject of request.")) if principal_type in (SERVICE, HOST): if cn.lower() != principal.hostname.lower(): raise errors.ACIError( info=_("hostname in subject of request '%(cn)s' " "does not match principal hostname '%(hostname)s'") % dict(cn=cn, hostname=principal.hostname)) elif principal_type == USER: # check user name if cn != principal.username: raise errors.ValidationError( name='csr', error=_("DN commonName does not match user's login") ) # check email address mail = subject.email_address #pylint: disable=E1101 if mail is not None and mail not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_( "DN emailAddress does not match " "any of user's email addresses") ) # We got this far so the principal entry exists, can we write it? if not ldap.can_write(dn, "usercertificate"): raise errors.ACIError(info=_("Insufficient 'write' privilege " "to the 'userCertificate' attribute of entry '%s'.") % dn) # Validate the subject alt name, if any for name_type, desc, name, der_name in subjectaltname: if name_type == nss.certDNSName: name = unicode(name) alt_principal = None alt_principal_obj = None try: if principal_type == HOST: alt_principal = kerberos.Principal( (u'host', name), principal.realm) alt_principal_obj = api.Command['host_show'](name, all=True) elif principal_type == SERVICE: alt_principal = kerberos.Principal( (principal.service_name, name), principal.realm) alt_principal_obj = api.Command['service_show']( alt_principal, all=True) elif principal_type == USER: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for user principals") % desc ) except errors.NotFound: # We don't want to issue any certificates referencing # machines we don't know about. Nothing is stored in this # host record related to this certificate. raise errors.NotFound(reason=_('The service principal for ' 'subject alt name %s in certificate request does not ' 'exist') % name) if alt_principal_obj is not None: altdn = alt_principal_obj['result']['dn'] if not ldap.can_write(altdn, "usercertificate"): raise errors.ACIError(info=_( "Insufficient privilege to create a certificate " "with subject alt name '%s'.") % name) if alt_principal is not None and not bypass_caacl: caacl_check(principal_type, alt_principal, ca, profile_id) elif name_type in [ (nss.certOtherName, x509.SAN_UPN), (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME), ]: if name != principal_string: raise errors.ACIError( info=_("Principal '%s' in subject alt name does not " "match requested principal") % name) elif name_type == nss.certRFC822Name: if principal_type == USER: if name not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_( "RFC822Name does not match " "any of user's email addresses") ) else: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for non-user principals") % desc ) else: raise errors.ACIError( info=_("Subject alt name type %s is forbidden") % desc) # Request the certificate try: result = self.Backend.ra.request_certificate( csr, profile_id, ca_id, request_type=request_type) except errors.HTTPRequestError as e: if e.status == 409: # pylint: disable=no-member raise errors.CertificateOperationError( error=_("CA '%s' is disabled") % ca) else: raise e if not raw: self.obj._parse(result, all) result['request_id'] = int(result['request_id']) result['cacn'] = ca_obj['cn'][0] # Success? Then add it to the principal's entry # (unless the profile tells us not to) profile = api.Command['certprofile_show'](profile_id) store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE' if store and 'certificate' in result: cert = str(result.get('certificate')) kwargs = dict(addattr=u'usercertificate={}'.format(cert)) if principal_type == SERVICE: api.Command['service_mod'](principal_string, **kwargs) elif principal_type == HOST: api.Command['host_mod'](principal.hostname, **kwargs) elif principal_type == USER: api.Command['user_mod'](principal.username, **kwargs) return dict( result=result, value=pkey_to_value(int(result['request_id']), kw), )
def execute(self, csr, all=False, raw=False, **kw): ca_enabled_check() ldap = self.api.Backend.ldap2 add = kw.get('add') request_type = kw.get('request_type') profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE) # Check that requested authority exists (done before CA ACL # enforcement so that user gets better error message if # referencing nonexistant CA) and look up authority ID. # ca = kw['cacn'] ca_obj = api.Command.ca_show(ca)['result'] ca_id = ca_obj['ipacaid'][0] """ Access control is partially handled by the ACI titled 'Hosts can modify service userCertificate'. This is for the case where a machine binds using a host/ prinicpal. It can only do the request if the target hostname is in the managedBy attribute which is managed using the add/del member commands. Binding with a user principal one needs to be in the request_certs taskgroup (directly or indirectly via role membership). """ principal = kw.get('principal') principal_string = unicode(principal) if principal.is_user: principal_type = USER elif principal.is_host: principal_type = HOST else: principal_type = SERVICE bind_principal = kerberos.Principal(getattr(context, 'principal')) bind_principal_string = unicode(bind_principal) if bind_principal.is_user: bind_principal_type = USER elif bind_principal.is_host: bind_principal_type = HOST else: bind_principal_type = SERVICE if (bind_principal_string != principal_string and bind_principal_type != HOST): # Can the bound principal request certs for another principal? self.check_access() try: self.check_access("request certificate ignore caacl") bypass_caacl = True except errors.ACIError: bypass_caacl = False if not bypass_caacl: caacl_check(principal_type, principal, ca, profile_id) try: subject = pkcs10.get_subject(csr) extensions = pkcs10.get_extensions(csr) subjectaltname = pkcs10.get_subjectaltname(csr) or () except (NSPRError, PyAsn1Error, ValueError) as e: raise errors.CertificateOperationError( error=_("Failure decoding Certificate Signing Request: %s") % e) # self-service and host principals may bypass SAN permission check if (bind_principal_string != principal_string and bind_principal_type != HOST): if '2.5.29.17' in extensions: self.check_access('request certificate with subjectaltname') dn = None principal_obj = None # See if the service exists and punt if it doesn't and we aren't # going to add it try: if principal_type == SERVICE: principal_obj = api.Command['service_show'](principal_string, all=True) elif principal_type == HOST: principal_obj = api.Command['host_show'](principal.hostname, all=True) elif principal_type == USER: principal_obj = api.Command['user_show'](principal.username, all=True) except errors.NotFound as e: if add: if principal_type == SERVICE: principal_obj = api.Command['service_add']( principal_string, force=True) else: princtype_str = PRINCIPAL_TYPE_STRING_MAP[principal_type] raise errors.OperationNotSupportedForPrincipalType( operation=_("'add' option"), principal_type=princtype_str) else: raise errors.NotFound( reason=_("The principal for this request doesn't exist.")) principal_obj = principal_obj['result'] dn = principal_obj['dn'] # Ensure that the DN in the CSR matches the principal cn = subject.common_name #pylint: disable=E1101 if not cn: raise errors.ValidationError( name='csr', error=_("No Common Name was found in subject of request.")) if principal_type in (SERVICE, HOST): if cn.lower() != principal.hostname.lower(): raise errors.ACIError(info=_( "hostname in subject of request '%(cn)s' " "does not match principal hostname '%(hostname)s'") % dict(cn=cn, hostname=principal.hostname)) elif principal_type == USER: # check user name if cn != principal.username: raise errors.ValidationError( name='csr', error=_("DN commonName does not match user's login")) # check email address mail = subject.email_address #pylint: disable=E1101 if mail is not None and mail not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_("DN emailAddress does not match " "any of user's email addresses")) # We got this far so the principal entry exists, can we write it? if not ldap.can_write(dn, "usercertificate"): raise errors.ACIError( info=_("Insufficient 'write' privilege " "to the 'userCertificate' attribute of entry '%s'.") % dn) # Validate the subject alt name, if any for name_type, desc, name, _der_name in subjectaltname: if name_type == nss.certDNSName: name = unicode(name) alt_principal = None alt_principal_obj = None try: if principal_type == HOST: alt_principal = kerberos.Principal((u'host', name), principal.realm) alt_principal_obj = api.Command['host_show'](name, all=True) elif principal_type == SERVICE: alt_principal = kerberos.Principal( (principal.service_name, name), principal.realm) alt_principal_obj = api.Command['service_show']( alt_principal, all=True) elif principal_type == USER: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for user principals") % desc) except errors.NotFound: # We don't want to issue any certificates referencing # machines we don't know about. Nothing is stored in this # host record related to this certificate. raise errors.NotFound(reason=_( 'The service principal for ' 'subject alt name %s in certificate request does not ' 'exist') % name) if alt_principal_obj is not None: altdn = alt_principal_obj['result']['dn'] if not ldap.can_write(altdn, "usercertificate"): raise errors.ACIError(info=_( "Insufficient privilege to create a certificate " "with subject alt name '%s'.") % name) if alt_principal is not None and not bypass_caacl: caacl_check(principal_type, alt_principal, ca, profile_id) elif name_type in [ (nss.certOtherName, x509.SAN_UPN), (nss.certOtherName, x509.SAN_KRB5PRINCIPALNAME), ]: if name != principal_string: raise errors.ACIError( info=_("Principal '%s' in subject alt name does not " "match requested principal") % name) elif name_type == nss.certRFC822Name: if principal_type == USER: if name not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_("RFC822Name does not match " "any of user's email addresses")) else: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for non-user principals") % desc) else: raise errors.ACIError( info=_("Subject alt name type %s is forbidden") % desc) # Request the certificate try: result = self.Backend.ra.request_certificate( csr, profile_id, ca_id, request_type=request_type) except errors.HTTPRequestError as e: if e.status == 409: # pylint: disable=no-member raise errors.CertificateOperationError( error=_("CA '%s' is disabled") % ca) else: raise e if not raw: self.obj._parse(result, all) result['request_id'] = int(result['request_id']) result['cacn'] = ca_obj['cn'][0] # Success? Then add it to the principal's entry # (unless the profile tells us not to) profile = api.Command['certprofile_show'](profile_id) store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE' if store and 'certificate' in result: cert = str(result.get('certificate')) kwargs = dict(addattr=u'usercertificate={}'.format(cert)) if principal_type == SERVICE: api.Command['service_mod'](principal_string, **kwargs) elif principal_type == HOST: api.Command['host_mod'](principal.hostname, **kwargs) elif principal_type == USER: api.Command['user_mod'](principal.username, **kwargs) return dict( result=result, value=pkey_to_value(int(result['request_id']), kw), )
def execute(self, csr, **kw): ca_enabled_check() ldap = self.api.Backend.ldap2 add = kw.get('add') request_type = kw.get('request_type') profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE) ca = '.' # top-level CA hardcoded until subca plugin implemented """ Access control is partially handled by the ACI titled 'Hosts can modify service userCertificate'. This is for the case where a machine binds using a host/ prinicpal. It can only do the request if the target hostname is in the managedBy attribute which is managed using the add/del member commands. Binding with a user principal one needs to be in the request_certs taskgroup (directly or indirectly via role membership). """ principal_string = kw.get('principal') principal = split_any_principal(principal_string) servicename, principal_name, realm = principal if servicename is None: principal_type = USER elif servicename == 'host': principal_type = HOST else: principal_type = SERVICE bind_principal = split_any_principal(getattr(context, 'principal')) bind_service, bind_name, bind_realm = bind_principal if bind_service is None: bind_principal_type = USER elif bind_service == 'host': bind_principal_type = HOST else: bind_principal_type = SERVICE if bind_principal != principal and bind_principal_type != HOST: # Can the bound principal request certs for another principal? self.check_access() try: self.check_access("request certificate ignore caacl") bypass_caacl = True except errors.ACIError: bypass_caacl = False if not bypass_caacl: caacl_check(principal_type, principal_string, ca, profile_id) try: subject = pkcs10.get_subject(csr) extensions = pkcs10.get_extensions(csr) subjectaltname = pkcs10.get_subjectaltname(csr) or () except (NSPRError, PyAsn1Error) as e: raise errors.CertificateOperationError( error=_("Failure decoding Certificate Signing Request: %s") % e) # self-service and host principals may bypass SAN permission check if bind_principal != principal and bind_principal_type != HOST: if '2.5.29.17' in extensions: self.check_access('request certificate with subjectaltname') dn = None principal_obj = None # See if the service exists and punt if it doesn't and we aren't # going to add it try: if principal_type == SERVICE: principal_obj = api.Command['service_show'](principal_string, all=True) elif principal_type == HOST: principal_obj = api.Command['host_show'](principal_name, all=True) elif principal_type == USER: principal_obj = api.Command['user_show'](principal_name, all=True) except errors.NotFound as e: if principal_type == SERVICE and add: principal_obj = api.Command['service_add'](principal_string, force=True) else: raise errors.NotFound( reason=_("The principal for this request doesn't exist.")) principal_obj = principal_obj['result'] dn = principal_obj['dn'] # Ensure that the DN in the CSR matches the principal cn = subject.common_name #pylint: disable=E1101 if not cn: raise errors.ValidationError(name='csr', error=_("No Common Name was found in subject of request.")) if principal_type in (SERVICE, HOST): if cn.lower() != principal_name.lower(): raise errors.ACIError( info=_("hostname in subject of request '%(cn)s' " "does not match principal hostname '%(hostname)s'") % dict(cn=cn, hostname=principal_name)) elif principal_type == USER: # check user name if cn != principal_name: raise errors.ValidationError( name='csr', error=_("DN commonName does not match user's login") ) # check email address mail = subject.email_address #pylint: disable=E1101 if mail is not None and mail not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_( "DN emailAddress does not match " "any of user's email addresses") ) # We got this far so the principal entry exists, can we write it? if not ldap.can_write(dn, "usercertificate"): raise errors.ACIError(info=_("Insufficient 'write' privilege " "to the 'userCertificate' attribute of entry '%s'.") % dn) # Validate the subject alt name, if any for name_type, name in subjectaltname: if name_type == pkcs10.SAN_DNSNAME: name = unicode(name) alt_principal_obj = None alt_principal_string = None try: if principal_type == HOST: alt_principal_string = 'host/%s@%s' % (name, realm) alt_principal_obj = api.Command['host_show'](name, all=True) elif principal_type == SERVICE: alt_principal_string = '%s/%s@%s' % (servicename, name, realm) alt_principal_obj = api.Command['service_show']( alt_principal_string, all=True) elif principal_type == USER: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for user principals") % name_type ) except errors.NotFound: # We don't want to issue any certificates referencing # machines we don't know about. Nothing is stored in this # host record related to this certificate. raise errors.NotFound(reason=_('The service principal for ' 'subject alt name %s in certificate request does not ' 'exist') % name) if alt_principal_obj is not None: altdn = alt_principal_obj['result']['dn'] if not ldap.can_write(altdn, "usercertificate"): raise errors.ACIError(info=_( "Insufficient privilege to create a certificate " "with subject alt name '%s'.") % name) if alt_principal_string is not None and not bypass_caacl: caacl_check( principal_type, alt_principal_string, ca, profile_id) elif name_type in (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME, pkcs10.SAN_OTHERNAME_UPN): if split_any_principal(name) != principal: raise errors.ACIError( info=_("Principal '%s' in subject alt name does not " "match requested principal") % name) elif name_type == pkcs10.SAN_RFC822NAME: if principal_type == USER: if name not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_( "RFC822Name does not match " "any of user's email addresses") ) else: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for non-user principals") % name_type ) else: raise errors.ACIError( info=_("Subject alt name type %s is forbidden") % name_type) # Request the certificate result = self.Backend.ra.request_certificate( csr, profile_id, request_type=request_type) cert = x509.load_certificate(result['certificate']) result['issuer'] = unicode(cert.issuer) result['valid_not_before'] = unicode(cert.valid_not_before_str) result['valid_not_after'] = unicode(cert.valid_not_after_str) result['md5_fingerprint'] = unicode(nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0]) result['sha1_fingerprint'] = unicode(nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0]) # Success? Then add it to the principal's entry # (unless the profile tells us not to) profile = api.Command['certprofile_show'](profile_id) store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE' if store and 'certificate' in result: cert = str(result.get('certificate')) kwargs = dict(addattr=u'usercertificate={}'.format(cert)) if principal_type == SERVICE: api.Command['service_mod'](principal_string, **kwargs) elif principal_type == HOST: api.Command['host_mod'](principal_name, **kwargs) elif principal_type == USER: api.Command['user_mod'](principal_name, **kwargs) return dict( result=result )
def execute(self, csr, **kw): ca_enabled_check() ldap = self.api.Backend.ldap2 add = kw.get('add') request_type = kw.get('request_type') profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE) ca = '.' # top-level CA hardcoded until subca plugin implemented """ Access control is partially handled by the ACI titled 'Hosts can modify service userCertificate'. This is for the case where a machine binds using a host/ prinicpal. It can only do the request if the target hostname is in the managedBy attribute which is managed using the add/del member commands. Binding with a user principal one needs to be in the request_certs taskgroup (directly or indirectly via role membership). """ principal_string = kw.get('principal') principal = split_any_principal(principal_string) servicename, principal_name, realm = principal if servicename is None: principal_type = USER elif servicename == 'host': principal_type = HOST else: principal_type = SERVICE bind_principal = split_any_principal(getattr(context, 'principal')) bind_service, bind_name, bind_realm = bind_principal if bind_service is None: bind_principal_type = USER elif bind_service == 'host': bind_principal_type = HOST else: bind_principal_type = SERVICE if bind_principal != principal and bind_principal_type != HOST: # Can the bound principal request certs for another principal? self.check_access() try: self.check_access("request certificate ignore caacl") bypass_caacl = True except errors.ACIError: bypass_caacl = False if not bypass_caacl: caacl_check(principal_type, principal_string, ca, profile_id) try: subject = pkcs10.get_subject(csr) extensions = pkcs10.get_extensions(csr) subjectaltname = pkcs10.get_subjectaltname(csr) or () except (NSPRError, PyAsn1Error, ValueError) as e: raise errors.CertificateOperationError( error=_("Failure decoding Certificate Signing Request: %s") % e) # self-service and host principals may bypass SAN permission check if bind_principal != principal and bind_principal_type != HOST: if '2.5.29.17' in extensions: self.check_access('request certificate with subjectaltname') dn = None principal_obj = None # See if the service exists and punt if it doesn't and we aren't # going to add it try: if principal_type == SERVICE: principal_obj = api.Command['service_show'](principal_string, all=True) elif principal_type == HOST: principal_obj = api.Command['host_show'](principal_name, all=True) elif principal_type == USER: principal_obj = api.Command['user_show'](principal_name, all=True) except errors.NotFound as e: if principal_type == SERVICE and add: principal_obj = api.Command['service_add'](principal_string, force=True) else: raise errors.NotFound( reason=_("The principal for this request doesn't exist.")) principal_obj = principal_obj['result'] dn = principal_obj['dn'] # Ensure that the DN in the CSR matches the principal cn = subject.common_name #pylint: disable=E1101 if not cn: raise errors.ValidationError( name='csr', error=_("No Common Name was found in subject of request.")) if principal_type in (SERVICE, HOST): if cn.lower() != principal_name.lower(): raise errors.ACIError(info=_( "hostname in subject of request '%(cn)s' " "does not match principal hostname '%(hostname)s'") % dict(cn=cn, hostname=principal_name)) elif principal_type == USER: # check user name if cn != principal_name: raise errors.ValidationError( name='csr', error=_("DN commonName does not match user's login")) # check email address mail = subject.email_address #pylint: disable=E1101 if mail is not None and mail not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_("DN emailAddress does not match " "any of user's email addresses")) # We got this far so the principal entry exists, can we write it? if not ldap.can_write(dn, "usercertificate"): raise errors.ACIError( info=_("Insufficient 'write' privilege " "to the 'userCertificate' attribute of entry '%s'.") % dn) # Validate the subject alt name, if any for name_type, name in subjectaltname: if name_type == pkcs10.SAN_DNSNAME: name = unicode(name) alt_principal_obj = None alt_principal_string = None try: if principal_type == HOST: alt_principal_string = 'host/%s@%s' % (name, realm) alt_principal_obj = api.Command['host_show'](name, all=True) elif principal_type == SERVICE: alt_principal_string = '%s/%s@%s' % (servicename, name, realm) alt_principal_obj = api.Command['service_show']( alt_principal_string, all=True) elif principal_type == USER: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for user principals") % name_type) except errors.NotFound: # We don't want to issue any certificates referencing # machines we don't know about. Nothing is stored in this # host record related to this certificate. raise errors.NotFound(reason=_( 'The service principal for ' 'subject alt name %s in certificate request does not ' 'exist') % name) if alt_principal_obj is not None: altdn = alt_principal_obj['result']['dn'] if not ldap.can_write(altdn, "usercertificate"): raise errors.ACIError(info=_( "Insufficient privilege to create a certificate " "with subject alt name '%s'.") % name) if alt_principal_string is not None and not bypass_caacl: caacl_check(principal_type, alt_principal_string, ca, profile_id) elif name_type in (pkcs10.SAN_OTHERNAME_KRB5PRINCIPALNAME, pkcs10.SAN_OTHERNAME_UPN): if split_any_principal(name) != principal: raise errors.ACIError( info=_("Principal '%s' in subject alt name does not " "match requested principal") % name) elif name_type == pkcs10.SAN_RFC822NAME: if principal_type == USER: if name not in principal_obj.get('mail', []): raise errors.ValidationError( name='csr', error=_("RFC822Name does not match " "any of user's email addresses")) else: raise errors.ValidationError( name='csr', error=_("subject alt name type %s is forbidden " "for non-user principals") % name_type) else: raise errors.ACIError( info=_("Subject alt name type %s is forbidden") % name_type) # Request the certificate result = self.Backend.ra.request_certificate(csr, profile_id, request_type=request_type) cert = x509.load_certificate(result['certificate']) result['issuer'] = unicode(cert.issuer) result['valid_not_before'] = unicode(cert.valid_not_before_str) result['valid_not_after'] = unicode(cert.valid_not_after_str) result['md5_fingerprint'] = unicode( nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0]) result['sha1_fingerprint'] = unicode( nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0]) # Success? Then add it to the principal's entry # (unless the profile tells us not to) profile = api.Command['certprofile_show'](profile_id) store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE' if store and 'certificate' in result: cert = str(result.get('certificate')) kwargs = dict(addattr=u'usercertificate={}'.format(cert)) if principal_type == SERVICE: api.Command['service_mod'](principal_string, **kwargs) elif principal_type == HOST: api.Command['host_mod'](principal_name, **kwargs) elif principal_type == USER: api.Command['user_mod'](principal_name, **kwargs) return dict(result=result)