mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
csrgen: fix when attribute shortname is lower case
OpenSSL requires attribute short names ("CN", "O", etc) to be in
upper case, otherwise it fails to add the attribute. This can be
triggered when FreeIPA has been installed with --subject-base
containing a lower-case attribute shortname (e.g.
--subject-base="o=Red Hat").
Explicitly convert the attribute type string to an OID
(ASN1_OBJECT *). If that fails, upper-case the type string and try
again.
Add some tests for the required behaviour.
Fixes: https://pagure.io/freeipa/issue/7496
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
committed by
Christian Heimes
parent
0ac1d3ea62
commit
852618fd65
@@ -59,6 +59,7 @@ void EVP_PKEY_free(EVP_PKEY *pkey);
|
||||
/* openssl/x509.h */
|
||||
typedef ... ASN1_INTEGER;
|
||||
typedef ... ASN1_BIT_STRING;
|
||||
typedef ... ASN1_OBJECT;
|
||||
typedef ... X509;
|
||||
typedef ... X509_ALGOR;
|
||||
typedef ... X509_CRL;
|
||||
@@ -86,11 +87,14 @@ X509_REQ *X509_REQ_new(void);
|
||||
void X509_REQ_free(X509_REQ *);
|
||||
EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a);
|
||||
int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
|
||||
int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type,
|
||||
int X509_NAME_add_entry_by_OBJ(X509_NAME *name, const ASN1_OBJECT *obj, int type,
|
||||
const unsigned char *bytes, int len, int loc,
|
||||
int set);
|
||||
int X509_NAME_entry_count(X509_NAME *name);
|
||||
int i2d_X509_REQ_INFO(X509_REQ_INFO *a, unsigned char **out); \
|
||||
int i2d_X509_REQ_INFO(X509_REQ_INFO *a, unsigned char **out);
|
||||
|
||||
/* openssl/objects.h */
|
||||
ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name);
|
||||
|
||||
/* openssl/x509v3.h */
|
||||
typedef ... X509V3_CONF_METHOD;
|
||||
@@ -114,7 +118,7 @@ int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section,
|
||||
/* openssl/x509v3.h */
|
||||
unsigned long ERR_get_error(void);
|
||||
char *ERR_error_string(unsigned long e, char *buf);
|
||||
''')
|
||||
''') # noqa: E501
|
||||
|
||||
_libcrypto = _ffi.dlopen(ctypes.util.find_library('crypto'))
|
||||
|
||||
@@ -154,13 +158,17 @@ X509_REQ_free = _libcrypto.X509_REQ_free
|
||||
X509_REQ_set_pubkey = _libcrypto.X509_REQ_set_pubkey
|
||||
d2i_PUBKEY_bio = _libcrypto.d2i_PUBKEY_bio
|
||||
i2d_X509_REQ_INFO = _libcrypto.i2d_X509_REQ_INFO
|
||||
X509_NAME_add_entry_by_txt = _libcrypto.X509_NAME_add_entry_by_txt
|
||||
X509_NAME_add_entry_by_OBJ = _libcrypto.X509_NAME_add_entry_by_OBJ
|
||||
X509_NAME_entry_count = _libcrypto.X509_NAME_entry_count
|
||||
|
||||
|
||||
def X509_REQ_get_subject_name(req):
|
||||
return req.req_info.subject
|
||||
|
||||
|
||||
# openssl/objects.h
|
||||
OBJ_txt2obj = _libcrypto.OBJ_txt2obj
|
||||
|
||||
# openssl/evp.h
|
||||
EVP_PKEY_free = _libcrypto.EVP_PKEY_free
|
||||
|
||||
@@ -209,8 +217,24 @@ def _parse_dn_section(subj, dn_sk):
|
||||
mval = -1
|
||||
else:
|
||||
mval = 0
|
||||
if not X509_NAME_add_entry_by_txt(
|
||||
subj, rdn_type, MBSTRING_UTF8,
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from ipaclient import csrgen
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography import x509
|
||||
|
||||
from ipaclient import csrgen, csrgen_ffi
|
||||
from ipalib import errors
|
||||
|
||||
BASE_DIR = os.path.dirname(__file__)
|
||||
@@ -201,6 +205,64 @@ class test_CSRGenerator(object):
|
||||
expected_script = f.read()
|
||||
assert script == expected_script
|
||||
|
||||
def test_works_with_lowercase_attr_type_shortname(self, generator):
|
||||
principal = {
|
||||
'uid': ['testuser'],
|
||||
'mail': ['testuser@example.com'],
|
||||
}
|
||||
template_env = {
|
||||
'ipacertificatesubjectbase': [
|
||||
'o=DOMAIN.EXAMPLE.COM' # lower-case attr type shortname
|
||||
],
|
||||
}
|
||||
config = generator.csr_config(principal, template_env, 'userCert')
|
||||
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend(),
|
||||
)
|
||||
adaptor = csrgen.OpenSSLAdaptor(key=key)
|
||||
|
||||
reqinfo = bytes(csrgen_ffi.build_requestinfo(
|
||||
config.encode('utf-8'), adaptor.get_subject_public_key_info()))
|
||||
csr_der = adaptor.sign_csr(reqinfo)
|
||||
csr = x509.load_der_x509_csr(csr_der, default_backend())
|
||||
assert (
|
||||
csr.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
|
||||
== [x509.NameAttribute(x509.NameOID.COMMON_NAME, u'testuser')]
|
||||
)
|
||||
assert (
|
||||
csr.subject.get_attributes_for_oid(x509.NameOID.ORGANIZATION_NAME)
|
||||
== [x509.NameAttribute(
|
||||
x509.NameOID.ORGANIZATION_NAME, u'DOMAIN.EXAMPLE.COM')]
|
||||
)
|
||||
|
||||
def test_unrecognised_attr_type_raises(self, generator):
|
||||
principal = {
|
||||
'uid': ['testuser'],
|
||||
'mail': ['testuser@example.com'],
|
||||
}
|
||||
template_env = {
|
||||
'ipacertificatesubjectbase': [
|
||||
'X=DOMAIN.EXAMPLE.COM' # unrecognised attr type
|
||||
],
|
||||
}
|
||||
config = generator.csr_config(principal, template_env, 'userCert')
|
||||
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend(),
|
||||
)
|
||||
adaptor = csrgen.OpenSSLAdaptor(key=key)
|
||||
|
||||
with pytest.raises(
|
||||
errors.CSRTemplateError,
|
||||
message='unrecognised attribute type: X'):
|
||||
csrgen_ffi.build_requestinfo(
|
||||
config.encode('utf-8'), adaptor.get_subject_public_key_info())
|
||||
|
||||
|
||||
class test_rule_handling(object):
|
||||
def test_optionalAttributeMissing(self, generator):
|
||||
|
||||
Reference in New Issue
Block a user