def required(self, data, name): if not data: raise errors.CSRTemplateError( reason=_( 'Required CSR generation rule %(name)s is missing data') % {'name': name}) return data
def _prepare_syntax_rule(self, syntax_rule, data_rules, description, data_sources): logger.debug('Syntax rule template: %s', syntax_rule.template) template = self.jinja2.from_string(syntax_rule.template, globals=self.passthrough_globals) is_required = syntax_rule.options.get('required', False) try: prepared_template = template.render(datarules=data_rules) except jinja2.UndefinedError: logger.debug(traceback.format_exc()) raise errors.CSRTemplateError( reason=_('Template error when formatting certificate data')) if data_sources: combinator = ' %s ' % syntax_rule.options.get( 'data_source_combinator', 'or') condition = combinator.join(data_sources) prepared_template = self._wrap_conditional(prepared_template, condition) if is_required: prepared_template = self._wrap_required(prepared_template, description) return prepared_template
def _raise_openssl_errors(): msgs = [] code = ERR_get_error() while code != 0: msg = ERR_error_string(code, NULL) msgs.append(_ffi.string(msg)) code = ERR_get_error() raise errors.CSRTemplateError(reason='\n'.join(msgs))
def _parse_dn_section(subj, dn_sk): for i in range(sk_CONF_VALUE_num(dn_sk)): v = sk_CONF_VALUE_value(dn_sk, i) rdn_type = _ffi.string(v.name) # Skip past any leading X. X: X, etc to allow for multiple instances for idx, c in enumerate(rdn_type): if c in b':,.': if idx+1 < len(rdn_type): rdn_type = rdn_type[idx+1:] break if rdn_type.startswith(b'+'): rdn_type = rdn_type[1:] mval = -1 else: mval = 0 # convert rdn_type to an OID # # OpenSSL is fussy about the case of the string. For example, # lower-case 'o' (for "organization name") is not recognised. # Therefore, try to convert the given string into an OID. If # that fails, convert it upper case and try again. # oid = OBJ_txt2obj(rdn_type, 0) if oid == NULL: oid = OBJ_txt2obj(rdn_type.upper(), 0) if oid == NULL: raise errors.CSRTemplateError( reason='unrecognised attribute type: {}' .format(rdn_type.decode('utf-8'))) if not X509_NAME_add_entry_by_OBJ( subj, oid, MBSTRING_UTF8, _ffi.cast("unsigned char *", v.value), -1, -1, mval): _raise_openssl_errors() if not X509_NAME_entry_count(subj): raise errors.CSRTemplateError( reason='error, subject in config file is empty')
def csr_config(self, principal, config, profile_id): render_data = {'subject': principal, 'config': config} rules = self.rule_provider.rules_for_profile(profile_id) template = self.formatter.build_template(rules) try: config = template.render(render_data) except jinja2.UndefinedError: logger.debug(traceback.format_exc()) raise errors.CSRTemplateError( reason=_('Template error when formatting certificate data')) return config
def _raise_openssl_errors(): msgs = [] code = ERR_get_error() while code != 0: msg = _ffi.string(ERR_error_string(code, NULL)) try: strmsg = msg.decode('utf-8') except UnicodeDecodeError: strmsg = repr(msg) msgs.append(strmsg) code = ERR_get_error() raise errors.CSRTemplateError(reason='\n'.join(msgs))
def csr_script(self, principal, config, profile_id, helper): render_data = {'subject': principal, 'config': config} formatter = self.FORMATTERS[helper]() rules = self.rule_provider.rules_for_profile(profile_id, helper) template = formatter.build_template(rules) try: script = template.render(render_data) except jinja2.UndefinedError: logger.debug(traceback.format_exc()) raise errors.CSRTemplateError(reason=_( 'Template error when formatting certificate data')) return script
def build_template(self, rules): """ Construct a template that can produce CSR generator strings. :param rules: list of FieldMapping to use to populate the template. :returns: jinja2.Template that can be rendered to produce the CSR data. """ syntax_rules = [] for field_mapping in rules: data_rules_prepared = [ self._prepare_data_rule(rule) for rule in field_mapping.data_rules ] data_sources = [] for xrule in field_mapping.data_rules: data_source = xrule.options.get('data_source') if data_source: data_sources.append(data_source) syntax_rules.append( self._prepare_syntax_rule(field_mapping.syntax_rule, data_rules_prepared, field_mapping.description, data_sources)) template_params = self._get_template_params(syntax_rules) base_template = self.jinja2.get_template( self.base_template_name, globals=self.passthrough_globals) try: combined_template_source = base_template.render(**template_params) except jinja2.UndefinedError: logger.debug(traceback.format_exc()) raise errors.CSRTemplateError( reason=_('Template error when formatting certificate data')) logger.debug('Formatting with template: %s', combined_template_source) combined_template = self.jinja2.from_string(combined_template_source) return combined_template
def _parse_dn_section(subj, dn_sk): for i in range(sk_CONF_VALUE_num(dn_sk)): v = sk_CONF_VALUE_value(dn_sk, i) rdn_type = _ffi.string(v.name) # Skip past any leading X. X: X, etc to allow for multiple instances for idx, c in enumerate(rdn_type): if c in ':,.': if idx+1 < len(rdn_type): rdn_type = rdn_type[idx+1:] break if rdn_type.startswith('+'): rdn_type = rdn_type[1:] mval = -1 else: mval = 0 if not X509_NAME_add_entry_by_txt( subj, rdn_type, MBSTRING_UTF8, v.value, -1, -1, mval): _raise_openssl_errors() if not X509_NAME_entry_count(subj): raise errors.CSRTemplateError( reason='error, subject in config file is empty')
def build_requestinfo(config, public_key_info): ''' Return a cffi buffer containing a DER-encoded CertificationRequestInfo. The returned object implements the buffer protocol. ''' reqdata = NULL req = NULL nconf_bio = NULL pubkey_bio = NULL pubkey = NULL try: reqdata = NCONF_new(NULL) if reqdata == NULL: _raise_openssl_errors() nconf_bio = BIO_new_mem_buf(config, len(config)) errorline = _ffi.new('long[1]', [-1]) i = NCONF_load_bio(reqdata, nconf_bio, errorline) if i < 0: if errorline[0] < 0: raise errors.CSRTemplateError(reason="Can't load config file") else: raise errors.CSRTemplateError( reason='Error on line %d of config file' % errorline[0]) dn_sect = NCONF_get_string(reqdata, b'req', b'distinguished_name') if dn_sect == NULL: raise errors.CSRTemplateError( reason='Unable to find "distinguished_name" key in config') dn_sk = NCONF_get_section(reqdata, dn_sect) if dn_sk == NULL: raise errors.CSRTemplateError( reason='Unable to find "%s" section in config' % _ffi.string(dn_sect)) pubkey_bio = BIO_new_mem_buf(public_key_info, len(public_key_info)) pubkey = d2i_PUBKEY_bio(pubkey_bio, NULL) if pubkey == NULL: _raise_openssl_errors() req = X509_REQ_new() if req == NULL: _raise_openssl_errors() subject = X509_REQ_get_subject_name(req) _parse_dn_section(subject, dn_sk) if not X509_REQ_set_pubkey(req, pubkey): _raise_openssl_errors() ext_ctx = _ffi.new("X509V3_CTX[1]") X509V3_set_ctx(ext_ctx, NULL, NULL, req, NULL, 0) X509V3_set_nconf(ext_ctx, reqdata) extn_section = NCONF_get_string(reqdata, b"req", b"req_extensions") if extn_section != NULL: if not X509V3_EXT_REQ_add_nconf( reqdata, ext_ctx, extn_section, req): _raise_openssl_errors() der_len = i2d_X509_REQ_INFO(req.req_info, NULL) if der_len < 0: _raise_openssl_errors() der_buf = _ffi.new("unsigned char[%d]" % der_len) der_out = _ffi.new("unsigned char **", der_buf) der_len = i2d_X509_REQ_INFO(req.req_info, der_out) if der_len < 0: _raise_openssl_errors() return _ffi.buffer(der_buf, der_len) finally: if reqdata != NULL: NCONF_free(reqdata) if req != NULL: X509_REQ_free(req) if nconf_bio != NULL: BIO_free(nconf_bio) if pubkey_bio != NULL: BIO_free(pubkey_bio) if pubkey != NULL: EVP_PKEY_free(pubkey)