mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-25 08:21:05 -06:00
79378c9051
Support both openssl 1.0 and 1.1 APIs where sk_* functions got prefixed with OPENSSL_ in the latter version. Since referencing a symbol from a dynamically loaded library generates exception, use the AttributeError exception to catch it and fall back to the older method. Fixes https://pagure.io/freeipa/issue/7110 Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
299 lines
8.4 KiB
Python
299 lines
8.4 KiB
Python
#!/usr/bin/python
|
|
|
|
from cffi import FFI
|
|
import ctypes.util
|
|
|
|
from ipalib import errors
|
|
|
|
_ffi = FFI()
|
|
|
|
_ffi.cdef('''
|
|
typedef ... CONF;
|
|
typedef ... CONF_METHOD;
|
|
typedef ... BIO;
|
|
typedef ... ipa_STACK_OF_CONF_VALUE;
|
|
|
|
/* openssl/conf.h */
|
|
typedef struct {
|
|
char *section;
|
|
char *name;
|
|
char *value;
|
|
} CONF_VALUE;
|
|
|
|
CONF *NCONF_new(CONF_METHOD *meth);
|
|
void NCONF_free(CONF *conf);
|
|
int NCONF_load_bio(CONF *conf, BIO *bp, long *eline);
|
|
ipa_STACK_OF_CONF_VALUE *NCONF_get_section(const CONF *conf,
|
|
const char *section);
|
|
char *NCONF_get_string(const CONF *conf, const char *group, const char *name);
|
|
|
|
/* openssl/safestack.h */
|
|
// int sk_CONF_VALUE_num(ipa_STACK_OF_CONF_VALUE *);
|
|
// CONF_VALUE *sk_CONF_VALUE_value(ipa_STACK_OF_CONF_VALUE *, int);
|
|
|
|
/* openssl/stack.h */
|
|
typedef ... _STACK;
|
|
|
|
int OPENSSL_sk_num(const _STACK *);
|
|
void *OPENSSL_sk_value(const _STACK *, int);
|
|
|
|
int sk_num(const _STACK *);
|
|
void *sk_value(const _STACK *, int);
|
|
|
|
/* openssl/bio.h */
|
|
BIO *BIO_new_mem_buf(const void *buf, int len);
|
|
int BIO_free(BIO *a);
|
|
|
|
/* openssl/asn1.h */
|
|
typedef struct ASN1_ENCODING_st {
|
|
unsigned char *enc; /* DER encoding */
|
|
long len; /* Length of encoding */
|
|
int modified; /* set to 1 if 'enc' is invalid */
|
|
} ASN1_ENCODING;
|
|
|
|
/* openssl/evp.h */
|
|
typedef ... EVP_PKEY;
|
|
|
|
void EVP_PKEY_free(EVP_PKEY *pkey);
|
|
|
|
/* openssl/x509.h */
|
|
typedef ... ASN1_INTEGER;
|
|
typedef ... ASN1_BIT_STRING;
|
|
typedef ... X509;
|
|
typedef ... X509_ALGOR;
|
|
typedef ... X509_CRL;
|
|
typedef ... X509_NAME;
|
|
typedef ... X509_PUBKEY;
|
|
typedef ... ipa_STACK_OF_X509_ATTRIBUTE;
|
|
|
|
typedef struct X509_req_info_st {
|
|
ASN1_ENCODING enc;
|
|
ASN1_INTEGER *version;
|
|
X509_NAME *subject;
|
|
X509_PUBKEY *pubkey;
|
|
/* d=2 hl=2 l= 0 cons: cont: 00 */
|
|
ipa_STACK_OF_X509_ATTRIBUTE *attributes; /* [ 0 ] */
|
|
} X509_REQ_INFO;
|
|
|
|
typedef struct X509_req_st {
|
|
X509_REQ_INFO *req_info;
|
|
X509_ALGOR *sig_alg;
|
|
ASN1_BIT_STRING *signature;
|
|
int references;
|
|
} X509_REQ;
|
|
|
|
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,
|
|
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); \
|
|
|
|
/* openssl/x509v3.h */
|
|
typedef ... X509V3_CONF_METHOD;
|
|
|
|
typedef struct v3_ext_ctx {
|
|
int flags;
|
|
X509 *issuer_cert;
|
|
X509 *subject_cert;
|
|
X509_REQ *subject_req;
|
|
X509_CRL *crl;
|
|
X509V3_CONF_METHOD *db_meth;
|
|
void *db;
|
|
} X509V3_CTX;
|
|
|
|
void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
|
|
X509_REQ *req, X509_CRL *crl, int flags);
|
|
void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf);
|
|
int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section,
|
|
X509_REQ *req);
|
|
|
|
/* openssl/x509v3.h */
|
|
unsigned long ERR_get_error(void);
|
|
char *ERR_error_string(unsigned long e, char *buf);
|
|
''')
|
|
|
|
_libcrypto = _ffi.dlopen(ctypes.util.find_library('crypto'))
|
|
|
|
NULL = _ffi.NULL
|
|
|
|
# openssl/conf.h
|
|
NCONF_new = _libcrypto.NCONF_new
|
|
NCONF_free = _libcrypto.NCONF_free
|
|
NCONF_load_bio = _libcrypto.NCONF_load_bio
|
|
NCONF_get_section = _libcrypto.NCONF_get_section
|
|
NCONF_get_string = _libcrypto.NCONF_get_string
|
|
|
|
# openssl/stack.h
|
|
try:
|
|
sk_num = _libcrypto.OPENSSL_sk_num
|
|
sk_value = _libcrypto.OPENSSL_sk_value
|
|
except AttributeError as e:
|
|
sk_num = _libcrypto.sk_num
|
|
sk_value = _libcrypto.sk_value
|
|
|
|
|
|
def sk_CONF_VALUE_num(sk):
|
|
return sk_num(_ffi.cast("_STACK *", sk))
|
|
|
|
|
|
def sk_CONF_VALUE_value(sk, i):
|
|
return _ffi.cast("CONF_VALUE *", sk_value(_ffi.cast("_STACK *", sk), i))
|
|
|
|
|
|
# openssl/bio.h
|
|
BIO_new_mem_buf = _libcrypto.BIO_new_mem_buf
|
|
BIO_free = _libcrypto.BIO_free
|
|
|
|
# openssl/x509.h
|
|
X509_REQ_new = _libcrypto.X509_REQ_new
|
|
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_entry_count = _libcrypto.X509_NAME_entry_count
|
|
|
|
|
|
def X509_REQ_get_subject_name(req):
|
|
return req.req_info.subject
|
|
|
|
# openssl/evp.h
|
|
EVP_PKEY_free = _libcrypto.EVP_PKEY_free
|
|
|
|
# openssl/asn1.h
|
|
MBSTRING_UTF8 = 0x1000
|
|
|
|
# openssl/x509v3.h
|
|
X509V3_set_ctx = _libcrypto.X509V3_set_ctx
|
|
X509V3_set_nconf = _libcrypto.X509V3_set_nconf
|
|
X509V3_EXT_REQ_add_nconf = _libcrypto.X509V3_EXT_REQ_add_nconf
|
|
|
|
# openssl/err.h
|
|
ERR_get_error = _libcrypto.ERR_get_error
|
|
ERR_error_string = _libcrypto.ERR_error_string
|
|
|
|
|
|
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 ':,.':
|
|
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):
|
|
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, 'req', '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, "req", "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)
|