Merge pull request #4829 from informatimago/smartcard-logon-rdp--x509-certificate-info-extraction

Smartcard Logon: restructured x509 certificate info extraction; added extracting the UPN.
This commit is contained in:
akallabeth 2018-08-27 14:33:09 +02:00 committed by GitHub
commit 9e3b48e0fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 731 additions and 130 deletions

View File

@ -42,12 +42,12 @@
struct crypto_cert_struct
{
X509 * px509;
STACK_OF(X509) *px509chain;
X509* px509;
STACK_OF(X509)* px509chain;
};
#ifdef __cplusplus
extern "C" {
extern "C" {
#endif
typedef struct crypto_cert_struct* CryptoCert;
@ -56,34 +56,51 @@ FREERDP_API CryptoCert crypto_cert_read(BYTE* data, UINT32 length);
FREERDP_API char* crypto_cert_fingerprint(X509* xcert);
FREERDP_API char* crypto_cert_subject(X509* xcert);
FREERDP_API char* crypto_cert_subject_common_name(X509* xcert, int* length);
FREERDP_API char** crypto_cert_subject_alt_name(X509* xcert, int* count,
int** lengths);
FREERDP_API void crypto_cert_subject_alt_name_free(int count, int *lengths,
char** alt_name);
FREERDP_API char** crypto_cert_get_dns_names(X509* xcert, int* count, int** lengths);
FREERDP_API char* crypto_cert_get_email(X509* x509);
FREERDP_API char* crypto_cert_get_upn(X509* x509);
FREERDP_API void crypto_cert_dns_names_free(int count, int* lengths, char** dns_names);
FREERDP_API char* crypto_cert_issuer(X509* xcert);
FREERDP_API void crypto_cert_print_info(X509* xcert);
FREERDP_API void crypto_cert_free(CryptoCert cert);
/*
Deprecated function names: crypto_cert_subject_alt_name and crypto_cert_subject_alt_name_free.
Use crypto_cert_get_dns_names and crypto_cert_dns_names_free instead.
(old names kept for now for compatibility of FREERDP_API).
Note: email and upn amongst others are also alt_names,
but the old crypto_cert_get_alt_names returned only the dns_names
*/
FREERDP_API char** crypto_cert_subject_alt_name(X509* xcert, int* count, int** lengths);
FREERDP_API void crypto_cert_subject_alt_name_free(int count, int *lengths, char** alt_names);
FREERDP_API BOOL x509_verify_certificate(CryptoCert cert, char* certificate_store_path);
FREERDP_API rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname, UINT16 port);
FREERDP_API BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey, DWORD* PublicKeyLength);
FREERDP_API rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname,
UINT16 port);
FREERDP_API BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey,
DWORD* PublicKeyLength);
#define TSSK_KEY_LENGTH 64
extern const BYTE tssk_modulus[];
extern const BYTE tssk_privateExponent[];
extern const BYTE tssk_exponent[];
FREERDP_API int crypto_rsa_public_encrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, BYTE* output);
FREERDP_API int crypto_rsa_public_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, BYTE* output);
FREERDP_API int crypto_rsa_private_encrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* private_exponent, BYTE* output);
FREERDP_API int crypto_rsa_private_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* private_exponent, BYTE* output);
FREERDP_API int crypto_rsa_public_encrypt(const BYTE* input, int length, UINT32 key_length,
const BYTE* modulus, const BYTE* exponent, BYTE* output);
FREERDP_API int crypto_rsa_public_decrypt(const BYTE* input, int length, UINT32 key_length,
const BYTE* modulus, const BYTE* exponent, BYTE* output);
FREERDP_API int crypto_rsa_private_encrypt(const BYTE* input, int length, UINT32 key_length,
const BYTE* modulus, const BYTE* private_exponent, BYTE* output);
FREERDP_API int crypto_rsa_private_decrypt(const BYTE* input, int length, UINT32 key_length,
const BYTE* modulus, const BYTE* private_exponent, BYTE* output);
FREERDP_API void crypto_reverse(BYTE* data, int length);
FREERDP_API char* crypto_base64_encode(const BYTE* data, int length);
FREERDP_API void crypto_base64_decode(const char* enc_data, int length, BYTE** dec_data, int* res_length);
FREERDP_API void crypto_base64_decode(const char* enc_data, int length, BYTE** dec_data,
int* res_length);
#ifdef __cplusplus
}
}
#endif
#endif /* FREERDP_CRYPTO_H */

View File

@ -32,11 +32,12 @@
CryptoCert crypto_cert_read(BYTE* data, UINT32 length)
{
CryptoCert cert = malloc(sizeof(*cert));
if (!cert)
return NULL;
/* this will move the data pointer but we don't care, we don't use it again */
cert->px509 = d2i_X509(NULL, (D2I_X509_CONST BYTE **) &data, length);
cert->px509 = d2i_X509(NULL, (D2I_X509_CONST BYTE**) &data, length);
return cert;
}
@ -46,7 +47,6 @@ void crypto_cert_free(CryptoCert cert)
return;
X509_free(cert->px509);
free(cert);
}
@ -56,8 +56,8 @@ BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey, DWORD* Public
int length;
BOOL status = TRUE;
EVP_PKEY* pkey = NULL;
pkey = X509_get_pubkey(cert->px509);
if (!pkey)
{
WLog_ERR(TAG, "X509_get_pubkey() failed");
@ -66,6 +66,7 @@ BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey, DWORD* Public
}
length = i2d_PublicKey(pkey, NULL);
if (length < 1)
{
WLog_ERR(TAG, "i2d_PublicKey() failed");
@ -75,7 +76,8 @@ BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey, DWORD* Public
*PublicKeyLength = (DWORD) length;
*PublicKey = (BYTE*) malloc(length);
ptr = (BYTE*) (*PublicKey);
ptr = (BYTE*)(*PublicKey);
if (!ptr)
{
status = FALSE;
@ -83,30 +85,30 @@ BOOL crypto_cert_get_public_key(CryptoCert cert, BYTE** PublicKey, DWORD* Public
}
i2d_PublicKey(pkey, &ptr);
exit:
if (pkey)
EVP_PKEY_free(pkey);
return status;
}
static int crypto_rsa_common(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, int exponent_size, BYTE* output)
static int crypto_rsa_common(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus,
const BYTE* exponent, int exponent_size, BYTE* output)
{
BN_CTX* ctx;
int output_length = -1;
BYTE* input_reverse;
BYTE* modulus_reverse;
BYTE* exponent_reverse;
BIGNUM *mod, *exp, *x, *y;
BIGNUM* mod, *exp, *x, *y;
input_reverse = (BYTE*) malloc(2 * key_length + exponent_size);
if (!input_reverse)
return -1;
modulus_reverse = input_reverse + key_length;
exponent_reverse = modulus_reverse + key_length;
memcpy(modulus_reverse, modulus, key_length);
crypto_reverse(modulus_reverse, key_length);
memcpy(exponent_reverse, exponent, exponent_size);
@ -133,7 +135,6 @@ static int crypto_rsa_common(const BYTE* input, int length, UINT32 key_length, c
BN_bin2bn(exponent_reverse, exponent_size, exp);
BN_bin2bn(input_reverse, length, x);
BN_mod_exp(y, x, exp, mod, ctx);
output_length = BN_bn2bin(y, output);
crypto_reverse(output, output_length);
@ -151,41 +152,47 @@ fail_bn_mod:
BN_CTX_free(ctx);
fail_bn_ctx:
free(input_reverse);
return output_length;
}
static int crypto_rsa_public(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, BYTE* output)
static int crypto_rsa_public(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus,
const BYTE* exponent, BYTE* output)
{
return crypto_rsa_common(input, length, key_length, modulus, exponent, EXPONENT_MAX_SIZE, output);
}
static int crypto_rsa_private(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* private_exponent, BYTE* output)
static int crypto_rsa_private(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus,
const BYTE* private_exponent, BYTE* output)
{
return crypto_rsa_common(input, length, key_length, modulus, private_exponent, key_length, output);
}
int crypto_rsa_public_encrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, BYTE* output)
int crypto_rsa_public_encrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus,
const BYTE* exponent, BYTE* output)
{
return crypto_rsa_public(input, length, key_length, modulus, exponent, output);
}
int crypto_rsa_public_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* exponent, BYTE* output)
int crypto_rsa_public_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus,
const BYTE* exponent, BYTE* output)
{
return crypto_rsa_public(input, length, key_length, modulus, exponent, output);
}
int crypto_rsa_private_encrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* private_exponent, BYTE* output)
int crypto_rsa_private_encrypt(const BYTE* input, int length, UINT32 key_length,
const BYTE* modulus, const BYTE* private_exponent, BYTE* output)
{
return crypto_rsa_private(input, length, key_length, modulus, private_exponent, output);
}
int crypto_rsa_private_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* private_exponent, BYTE* output)
int crypto_rsa_private_decrypt(const BYTE* input, int length, UINT32 key_length,
const BYTE* modulus, const BYTE* private_exponent, BYTE* output)
{
return crypto_rsa_private(input, length, key_length, modulus, private_exponent, output);
}
int crypto_rsa_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus, const BYTE* private_exponent, BYTE* output)
int crypto_rsa_decrypt(const BYTE* input, int length, UINT32 key_length, const BYTE* modulus,
const BYTE* private_exponent, BYTE* output)
{
return crypto_rsa_common(input, length, key_length, modulus, private_exponent, key_length, output);
}
@ -210,21 +217,21 @@ char* crypto_cert_fingerprint(X509* xcert)
char* fp_buffer;
UINT32 fp_len;
BYTE fp[EVP_MAX_MD_SIZE];
X509_digest(xcert, EVP_sha1(), fp, &fp_len);
fp_buffer = (char*) calloc(fp_len, 3);
if (!fp_buffer)
return NULL;
p = fp_buffer;
for (i = 0; i < (int) (fp_len - 1); i++)
for (i = 0; i < (int)(fp_len - 1); i++)
{
sprintf(p, "%02"PRIx8":", fp[i]);
p = &fp_buffer[(i + 1) * 3];
}
sprintf(p, "%02"PRIx8"", fp[i]);
sprintf(p, "%02"PRIx8"", fp[i]);
return fp_buffer;
}
@ -237,13 +244,14 @@ char* crypto_print_name(X509_NAME* name)
{
unsigned long size = BIO_number_written(outBIO);
buffer = calloc(1, size + 1);
if (!buffer)
return NULL;
BIO_read(outBIO, buffer, size);
}
BIO_free(outBIO);
return buffer;
}
@ -260,7 +268,6 @@ char* crypto_cert_subject_common_name(X509* xcert, int* length)
X509_NAME* subject_name;
X509_NAME_ENTRY* entry;
ASN1_STRING* entry_data;
subject_name = X509_get_subject_name(xcert);
if (subject_name == NULL)
@ -288,88 +295,459 @@ char* crypto_cert_subject_common_name(X509* xcert, int* length)
common_name = _strdup((char*)common_name_raw);
OPENSSL_free(common_name_raw);
return (char*) common_name;
}
void crypto_cert_subject_alt_name_free(int count, int *lengths,
char** alt_name)
/* GENERAL_NAME type labels */
static const char* general_name_type_labels[] = { "OTHERNAME",
"EMAIL ",
"DNS ",
"X400 ",
"DIRNAME ",
"EDIPARTY ",
"URI ",
"IPADD ",
"RID "
};
static const char* general_name_type_label(int general_name_type)
{
if ((0 <= general_name_type)
&& (general_name_type < ARRAYSIZE(general_name_type_labels)))
{
return general_name_type_labels[general_name_type];
}
else
{
static char buffer[80];
sprintf(buffer, "Unknown general name type (%d)", general_name_type);
return buffer;
}
}
/*
map_subject_alt_name(x509, general_name_type, mapper, data)
Call the function mapper with subjectAltNames found in the x509
certificate and data. if generate_name_type is GEN_ALL, the the
mapper is called for all the names, else it's called only for names
of the given type.
We implement two extractors:
- a string extractor that can be used to get the subjectAltNames of
the following types: GEN_URI, GEN_DNS, GEN_EMAIL
- a ASN1_OBJECT filter/extractor that can be used to get the
subjectAltNames of OTHERNAME type.
Note: usually, it's a string, but some type of otherNames can be
associated with different classes of objects. eg. a KPN may be a
sequence of realm and principal name, instead of a single string
object.
Not implemented yet: extractors for the types: GEN_X400, GEN_DIRNAME,
GEN_EDIPARTY, GEN_RID, GEN_IPADD (the later can contain nul-bytes).
mapper(name, data, index, count)
The mapper is passed:
- the GENERAL_NAME selected,
- the data,
- the index of the general name in the subjectAltNames,
- the total number of names in the subjectAltNames.
The last parameter let's the mapper allocate arrays to collect objects.
Note: if names are filtered, not all the indices from 0 to count-1 are
passed to mapper, only the indices selected.
When the mapper returns 0, map_subject_alt_name stops the iteration immediately.
*/
#define GEN_ALL (-1)
typedef int (*general_name_mapper_pr)(GENERAL_NAME* name, void* data, int index, int count);
static void map_subject_alt_name(X509* x509, int general_name_type, general_name_mapper_pr mapper,
void* data)
{
int i;
int num;
STACK_OF(GENERAL_NAME) *gens;
gens = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL);
if (!gens)
{
return;
}
num = sk_GENERAL_NAME_num(gens);
for (i = 0; (i < num); i++)
{
GENERAL_NAME* name = sk_GENERAL_NAME_value(gens, i);
if (name)
{
if ((general_name_type == GEN_ALL) || (general_name_type == name->type))
{
if (!mapper(name, data, i, num))
{
break;
}
}
}
}
sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
}
/*
extract_string -- string extractor
- the strings array is allocated lazily, when we first have to store a
string.
- allocated contains the size of the strings array, or -1 if
allocation failed.
- count contains the actual count of strings in the strings array.
- maximum limits the number of strings we can store in the strings
array: beyond, the extractor returns 0 to short-cut the search.
extract_string stores in the string list OPENSSL strings,
that must be freed with OPENSSL_free.
*/
typedef struct string_list
{
char** strings;
int allocated;
int count;
int maximum;
} string_list;
static void string_list_initialize(string_list* list)
{
list->strings = 0;
list->allocated = 0;
list->count = 0;
list->maximum = INT_MAX;
}
static void string_list_allocate(string_list* list, int allocate_count)
{
if (!list->strings && list->allocated == 0)
{
list->strings = calloc(allocate_count, sizeof(list->strings[0]));
list->allocated = list->strings ? allocate_count : -1;
list->count = 0;
}
}
static void string_list_free(string_list* list)
{
/* Note: we don't free the contents of the strings array: this */
/* is handled by the caller, either by returning this */
/* content, or freeing it itself. */
free(list->strings);
}
static int extract_string(GENERAL_NAME* name, void* data, int index, int count)
{
string_list* list = data;
unsigned char* cstring = 0;
ASN1_STRING* str;
switch (name->type)
{
case GEN_URI:
str = name->d.uniformResourceIdentifier;
break;
case GEN_DNS:
str = name->d.dNSName;
break;
case GEN_EMAIL:
str = name->d.rfc822Name;
break;
default:
return 1;
}
if ((ASN1_STRING_to_UTF8(&cstring, str)) < 0)
{
WLog_ERR(TAG, "ASN1_STRING_to_UTF8() failed for %s: %s",
general_name_type_label(name->type),
ERR_error_string(ERR_get_error(), NULL));
return 1;
}
string_list_allocate(list, count);
if (list->allocated <= 0)
{
OPENSSL_free(cstring);
return 0;
}
list->strings[list->count] = (char*)cstring;
list->count ++ ;
if (list->count >= list->maximum)
{
return 0;
}
return 1;
}
/*
extract_othername_object -- object extractor.
- the objects array is allocated lazily, when we first have to store a
string.
- allocated contains the size of the objects array, or -1 if
allocation failed.
- count contains the actual count of objects in the objects array.
- maximum limits the number of objects we can store in the objects
array: beyond, the extractor returns 0 to short-cut the search.
extract_othername_objects stores in the objects array ASN1_TYPE *
pointers directly obtained from the GENERAL_NAME.
*/
typedef struct object_list
{
ASN1_OBJECT* type_id;
char** strings;
int allocated;
int count;
int maximum;
} object_list;
static void object_list_initialize(object_list* list)
{
list->type_id = 0;
list->strings = 0;
list->allocated = 0;
list->count = 0;
list->maximum = INT_MAX;
}
static void object_list_allocate(object_list* list, int allocate_count)
{
if (!list->strings && list->allocated == 0)
{
list->strings = calloc(allocate_count, sizeof(list->strings[0]));
list->allocated = list->strings ? allocate_count : -1;
list->count = 0;
}
}
static char* object_string(ASN1_TYPE* object)
{
char* result;
unsigned char* utf8String;
int length;
/* TODO: check that object.type is a string type. */
length = ASN1_STRING_to_UTF8(& utf8String, object->value.asn1_string);
if (length < 0)
{
return 0;
}
result = (char*)strdup((char*)utf8String);
OPENSSL_free(utf8String);
return result;
}
static void object_list_free(object_list* list)
{
free(list->strings);
}
static int extract_othername_object_as_string(GENERAL_NAME* name, void* data, int index, int count)
{
object_list* list = data;
if (name->type != GEN_OTHERNAME)
{
return 1;
}
if (0 != OBJ_cmp(name->d.otherName->type_id, list->type_id))
{
return 1;
}
object_list_allocate(list, count);
if (list->allocated <= 0)
{
return 0;
}
list->strings[list->count] = object_string(name->d.otherName->value);
if (list->strings[list->count])
{
list->count ++ ;
}
if (list->count >= list->maximum)
{
return 0;
}
return 1;
}
/*
crypto_cert_get_email returns a dynamically allocated copy of the
first email found in the subjectAltNames (use free to free it).
*/
char* crypto_cert_get_email(X509* x509)
{
char* result = 0;
string_list list;
string_list_initialize(&list);
list.maximum = 1;
map_subject_alt_name(x509, GEN_EMAIL, extract_string, &list);
if (list.count == 0)
{
string_list_free(&list);
return 0;
}
result = strdup(list.strings[0]);
OPENSSL_free(list.strings[0]);
string_list_free(&list);
return result;
}
/*
crypto_cert_get_upn returns a dynamically allocated copy of the
first UPN otherNames in the subjectAltNames (use free to free it).
Note: if this first UPN otherName is not a string, then 0 is returned,
instead of searching for another UPN that would be a string.
*/
char* crypto_cert_get_upn(X509* x509)
{
char* result = 0;
object_list list;
object_list_initialize(&list);
list.type_id = OBJ_nid2obj(NID_ms_upn);
list.maximum = 1;
map_subject_alt_name(x509, GEN_OTHERNAME, extract_othername_object_as_string, &list);
if (list.count == 0)
{
object_list_free(&list);
return 0;
}
result = list.strings[0];
object_list_free(&list);
return result;
}
/* Deprecated name.*/
void crypto_cert_subject_alt_name_free(int count, int *lengths, char** alt_names)
{
crypto_cert_dns_names_free(count, lengths, alt_names);
}
void crypto_cert_dns_names_free(int count, int* lengths,
char** dns_names)
{
free(lengths);
if (alt_name)
if (dns_names)
{
for (i=0; i<count; i++)
int i;
for (i = 0; i < count; i++)
{
if (alt_name[i])
OPENSSL_free(alt_name[i]);
if (dns_names[i])
{
OPENSSL_free(dns_names[i]);
}
}
free(alt_name);
free(dns_names);
}
}
/* Deprecated name.*/
char** crypto_cert_subject_alt_name(X509* xcert, int* count, int** lengths)
{
int index;
int length = 0;
char** strings = NULL;
BYTE* string;
int num_subject_alt_names;
GENERAL_NAMES* subject_alt_names;
GENERAL_NAME* subject_alt_name;
*count = 0;
subject_alt_names = X509_get_ext_d2i(xcert, NID_subject_alt_name, 0, 0);
if (!subject_alt_names)
return NULL;
num_subject_alt_names = sk_GENERAL_NAME_num(subject_alt_names);
if (num_subject_alt_names)
{
strings = (char**) calloc(num_subject_alt_names, sizeof(char*));
if (!strings)
goto out;
*lengths = (int*) calloc(num_subject_alt_names, sizeof(int));
if (!*lengths)
{
free(strings);
strings = NULL;
goto out;
}
}
for (index = 0; index < num_subject_alt_names; ++index)
{
subject_alt_name = sk_GENERAL_NAME_value(subject_alt_names, index);
if (subject_alt_name->type == GEN_DNS)
{
length = ASN1_STRING_to_UTF8(&string, subject_alt_name->d.dNSName);
strings[*count] = (char*) string;
(*lengths)[*count] = length;
(*count)++;
}
}
if (*count < 1)
{
free(strings) ;
free(*lengths) ;
*lengths = NULL ;
return NULL;
}
out:
GENERAL_NAMES_free(subject_alt_names);
return strings;
return crypto_cert_get_dns_names(xcert, count, lengths);
}
char** crypto_cert_get_dns_names(X509* x509, int* count, int** lengths)
{
int i;
char** result = 0;
string_list list;
string_list_initialize(&list);
map_subject_alt_name(x509, GEN_DNS, extract_string, &list);
(*count) = list.count;
if (list.count == 0)
{
string_list_free(&list);
return 0;
}
/* lengths are not useful, since we converted the
strings to utf-8, there cannot be nul-bytes in them. */
result = calloc(list.count, sizeof(*result));
(*lengths) = calloc(list.count, sizeof(**lengths));
if (!result || !(*lengths))
{
free(result);
free(*lengths);
(*lengths) = 0;
(*count) = 0;
return result;
}
for (i = 0; i < list.count; i ++)
{
result[i] = list.strings[i];
(*lengths)[i] = strlen(result[i]);
}
string_list_free(&list);
return result;
}
char* crypto_cert_issuer(X509* xcert)
{
return crypto_print_name(X509_get_issuer_name(xcert));
@ -382,7 +760,6 @@ BOOL x509_verify_certificate(CryptoCert cert, char* certificate_store_path)
X509_STORE* cert_ctx = NULL;
X509_LOOKUP* lookup = NULL;
X509* xcert = cert->px509;
cert_ctx = X509_STORE_new();
if (cert_ctx == NULL)
@ -392,10 +769,9 @@ BOOL x509_verify_certificate(CryptoCert cert, char* certificate_store_path)
OpenSSL_add_all_algorithms();
#else
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
| OPENSSL_INIT_ADD_ALL_DIGESTS \
| OPENSSL_INIT_LOAD_CONFIG, NULL);
| OPENSSL_INIT_ADD_ALL_DIGESTS \
| OPENSSL_INIT_LOAD_CONFIG, NULL);
#endif
lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_file());
if (lookup == NULL)
@ -428,7 +804,6 @@ BOOL x509_verify_certificate(CryptoCert cert, char* certificate_store_path)
X509_STORE_CTX_free(csc);
X509_STORE_free(cert_ctx);
end:
return status;
}
@ -439,20 +814,17 @@ rdpCertificateData* crypto_get_certificate_data(X509* xcert, char* hostname, UIN
char* subject;
char* fp;
rdpCertificateData* certdata;
fp = crypto_cert_fingerprint(xcert);
if (!fp)
return NULL;
issuer = crypto_cert_issuer(xcert);
subject = crypto_cert_subject(xcert);
certdata = certificate_data_new(hostname, port, issuer, subject, fp);
free(subject);
free(issuer);
free(fp);
return certdata;
}
@ -461,10 +833,10 @@ void crypto_cert_print_info(X509* xcert)
char* fp;
char* issuer;
char* subject;
subject = crypto_cert_subject(xcert);
issuer = crypto_cert_issuer(xcert);
fp = crypto_cert_fingerprint(xcert);
if (!fp)
{
WLog_ERR(TAG, "error computing fingerprint");
@ -475,9 +847,10 @@ void crypto_cert_print_info(X509* xcert)
WLog_INFO(TAG, "\tSubject: %s", subject);
WLog_INFO(TAG, "\tIssuer: %s", issuer);
WLog_INFO(TAG, "\tThumbprint: %s", fp);
WLog_INFO(TAG, "The above X.509 certificate could not be verified, possibly because you do not have "
"the CA certificate in your certificate store, or the certificate has expired. "
"Please look at the OpenSSL documentation on how to add a private CA to the store.");
WLog_INFO(TAG,
"The above X.509 certificate could not be verified, possibly because you do not have "
"the CA certificate in your certificate store, or the certificate has expired. "
"Please look at the OpenSSL documentation on how to add a private CA to the store.");
free(fp);
out_free_issuer:
free(issuer);

View File

@ -6,7 +6,8 @@ set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestKnownHosts.c
TestBase64.c)
TestBase64.c
Test_x509_cert_info.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS
${${MODULE_PREFIX}_DRIVER}
@ -16,7 +17,7 @@ include_directories(${OPENSSL_INCLUDE_DIR})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} freerdp winpr)
target_link_libraries(${MODULE_NAME} freerdp winpr ${OPENSSL_LIBRARIES})
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")

View File

@ -0,0 +1,169 @@
#include <freerdp/crypto/crypto.h>
typedef char* (*get_field_pr)(X509*);
typedef struct
{
enum
{
DISABLED, ENABLED,
} status;
const char* field_description;
get_field_pr get_field;
const char* expected_result;
} certificate_test_t;
char* crypto_cert_subject_common_name_wo_length(X509* xcert)
{
int length;
return crypto_cert_subject_common_name(xcert, & length);
}
const char* certificate_path()
{
/*
Assume the .pem file is in the same directory as this source file.
Assume that __FILE__ will be a valid path to this file, even from the current working directory where the tests are run.
(ie. no chdir occurs between compilation and test running, or __FILE__ is an absolute path).
*/
#if defined(_WIN32)
static const char dirsep = '\\';
#else
static const char dirsep = '/';
#endif
static const char * filename = "Test_x509_cert_info.pem";
const char * file = __FILE__;
const char * last_dirsep = strrchr(file, dirsep);
if (last_dirsep)
{
char * result = malloc(last_dirsep - file + 1 + strlen(filename) + 1);
strncpy(result, file, (last_dirsep - file + 1));
strcpy(result + (last_dirsep - file + 1), filename);
return result;
}
else
{
/* No dirsep => relative path in same directory */
return filename;
}
}
const certificate_test_t certificate_tests[] =
{
{
ENABLED,
"Certificate Common Name",
crypto_cert_subject_common_name_wo_length,
"TESTJEAN TESTMARTIN 9999999"
},
{
ENABLED,
"Certificate subject",
crypto_cert_subject,
"CN = TESTJEAN TESTMARTIN 9999999, C = FR, O = MINISTERE DES TESTS, OU = 0002 110014016, OU = PERSONNES, UID = 9999999, GN = TESTJEAN, SN = TESTMARTIN"
},
{
DISABLED,
"Kerberos principal name",
0,
"testjean.testmartin@kpn.test.example.com"
},
{
ENABLED,
"Certificate e-mail",
crypto_cert_get_email,
"testjean.testmartin@test.example.com"
},
{
ENABLED,
"Microsoft's Universal Principal Name",
crypto_cert_get_upn,
"testjean.testmartin.9999999@upn.test.example.com"
},
{
ENABLED,
"Certificate issuer",
crypto_cert_issuer,
"CN = ADMINISTRATION CENTRALE DES TESTS, C = FR, O = MINISTERE DES TESTS, OU = 0002 110014016"
},
};
int TestCertificateFile(const char* certificate_path, const certificate_test_t* certificate_tests,
int count)
{
X509* certificate;
FILE* certificate_file = fopen(certificate_path, "r");
int success = 0;
int i;
if (!certificate_file)
{
printf("%s: failure: cannot open certificate file '%s'\n", __FUNCTION__, certificate_path);
return -1;
}
certificate = PEM_read_X509(certificate_file, 0, 0, 0);
if (!certificate)
{
printf("%s: failure: cannot read certificate file '%s'\n", __FUNCTION__, certificate_path);
success = -1;
goto fail;
}
for (i = 0; i < count; i ++)
{
char* result;
if (certificate_tests[i].status == DISABLED)
{
continue;
}
result = (certificate_tests[i].get_field
? certificate_tests[i].get_field(certificate)
: 0);
if (result)
{
printf("%s: crypto got %-40s -> \"%s\"\n", __FUNCTION__,
certificate_tests[i].field_description,
result);
if (0 != strcmp(result, certificate_tests[i].expected_result))
{
printf("%s: failure: for %s, actual: \"%s\", expected \"%s\"\n",
__FUNCTION__,
certificate_tests[i].field_description,
result,
certificate_tests[i].expected_result);
success = -1;
}
}
else
{
printf("%s: failure: cannot get %s\n", __FUNCTION__,
certificate_tests[i].field_description);
}
}
fail:
fclose(certificate_file);
return success;
}
int Test_x509_cert_info(int argc, char* argv[])
{
return TestCertificateFile(certificate_path(), certificate_tests, ARRAYSIZE(certificate_tests));
}

View File

@ -0,0 +1,41 @@
-----BEGIN CERTIFICATE-----
MIIHNzCCBR+gAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwcDEqMCgGA1UEAwwhQURN
SU5JU1RSQVRJT04gQ0VOVFJBTEUgREVTIFRFU1RTMQswCQYDVQQGEwJGUjEcMBoG
A1UECgwTTUlOSVNURVJFIERFUyBURVNUUzEXMBUGA1UECwwOMDAwMiAxMTAwMTQw
MTYwHhcNMTgwNTE4MDkyNTU1WhcNMTkwNTEzMDkyNTU1WjCBvzEkMCIGA1UEAwwb
VEVTVEpFQU4gVEVTVE1BUlRJTiA5OTk5OTk5MQswCQYDVQQGEwJGUjEcMBoGA1UE
CgwTTUlOSVNURVJFIERFUyBURVNUUzEXMBUGA1UECwwOMDAwMiAxMTAwMTQwMTYx
EjAQBgNVBAsMCVBFUlNPTk5FUzEXMBUGCgmSJomT8ixkAQEMBzk5OTk5OTkxETAP
BgNVBCoMCFRFU1RKRUFOMRMwEQYDVQQEDApURVNUTUFSVElOMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEA3yc22RDYc+Vc6F26/LONvaYkdTVDiCgbh9Ik
6pLF5izNpfdQ/YZU25h/UPECdchYX31UEErVOYudOBOHtU4fNjTO0oK5Va/DoFln
LnfwNpAlBZfogG+yy8fK4yLxG+raoSKDR/5P3hmTqKJqw1WpkwcVE2EDqkP1clMZ
L5cvJj6gLJa2q0JCdoKe7NntZkgpIk5ZHUZm2JYC30xL7XHfvvb/i0OZLpPOIekT
DCzxr9HTjbqe+BRZix2UiGpXzjIlDm6EEQNebZqf5kKgcbkxIDWcVraE0kO3TqJI
P4FBUeuxLqGwQ0AMKrZ+j8U7KAoM9WUoIFcmm8nYGo4hT6ugNIQ9nwQSgyH3yGH1
PU2k12Ovv2Ft8C/IFuusXxTOJprcFxtjE7qYZ44tmvlozlDOBOJYjLiURAh3r5LL
TadgArZ3XVMyWlwlTEy9qX59izY9Zz27kd5H11DOz5ezopHAWwP6sgCvWeNDyx8Q
I3jY8TYzJHahN2bknP2fqwwdGqFCrHItJx2DhDe2ruTk6vvbnwGgYqGzv+RtdNbW
CL4IMEQQKG9AM40WCz9pu32/vOaQ+hrYyCQMCtli0DSauB+K2IFPsAcz5OAaITJv
LenMt8mUP9NWHWfr5WYm0tuUCCU4dUT38MqkkdQv7oly1LHkvUdMU+Nk/Ki0Q83U
9gMvaPcCAwEAAaOCAYkwggGFMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXg
MDIGA1UdJQQrMCkGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQCAgYHKwYB
BQIDBDAdBgNVHQ4EFgQUXs4RKN+vUVsZjEW/J6qo6EZTLZUwHwYDVR0jBBgwFoAU
fUXj4k7OA3d8KylcprhMptiOL10wgbIGA1UdEQSBqjCBp6A9BgYrBgEFAgKgMzAx
oBYbFGtwbi50ZXN0LmV4YW1wbGUuY29toRcwFRsTdGVzdGplYW4udGVzdG1hcnRp
bqBABgorBgEEAYI3FAIDoDIMMHRlc3RqZWFuLnRlc3RtYXJ0aW4uOTk5OTk5OUB1
cG4udGVzdC5leGFtcGxlLmNvbYEkdGVzdGplYW4udGVzdG1hcnRpbkB0ZXN0LmV4
YW1wbGUuY29tMDEGA1UdEgQqMCiCEnJvb3RjYS5leGFtcGxlLmNvbYISaW50ZWNh
LmV4YW1wbGUuY29tMAkGA1UdIAQCMAAwDQYJKoZIhvcNAQEFBQADggIBAKRDovf+
CCnV2mtXnzH5JlduOjPWJmGB5a8HLPvakfAm4wQ0YyAViE1tar0V9lhG6nCogWWa
28D+eM5vLPjVE8ebq5UjIv76x6gWoJkQ3HtfVJvn9UfXwax6IqT7hb1fAHBqu0rj
uSnSxf1wIzPMp9Lb5x3jBu9ryNMiLUzeY1slBvosOXKlmprPhGWfPYYNCZo2bGJI
1w5alGDgTBcWKl7icJjAIuCpyRTnKCsaN3kyDU7C5aUhsm9AriPiNErzRI+l5+eu
Ywg3MZ7Yfjd3rXb6JleT0ZnCh/nFtVLIccWaI4phCrYTGz6odNIqrZ6X23Pt6Rx3
ZbQjtj4ipMdvbvJbS90aFMrTyfqhVLOxHy+setDcmPOixUgXlx8ZjFI9vgFUeJbo
OKrkLw4ITUduO+9MplBX7Kt/iCS/CbTfPlHMv03Xb6rbjqHxTJZCCu5QMNHiBeHV
l8FK5R6gv+9FuCl8uPHwGh/jelQp51cVORlQWeKpqWdwTi0Q3VeVeQAG5RR34xgT
cQa8h9AqkxYajhxKUmbUlaoYGd8TwUQLrS2jZxp/9geyApVQLAQ27CyAK5HyHSCA
uqCKsM0gFQyCL4IbXQyFMWgjXZYaorHFjVuMhYEkgWui/9sv+7sMAV5JzROeAw3l
4+D7yhywwuRzH2SzoavzGpWGMUveVsdLMRk9
-----END CERTIFICATE-----

View File

@ -1295,9 +1295,9 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
int index;
char* common_name = NULL;
int common_name_length = 0;
char** alt_names = NULL;
int alt_names_count = 0;
int* alt_names_lengths = NULL;
char** dns_names = 0;
int dns_names_count = 0;
int* dns_names_lengths = NULL;
BOOL certificate_status;
BOOL hostname_match = FALSE;
BOOL verification_status = FALSE;
@ -1367,8 +1367,8 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
certificate_data = crypto_get_certificate_data(cert->px509, hostname, port);
/* extra common name and alternative names */
common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length);
alt_names = crypto_cert_subject_alt_name(cert->px509, &alt_names_count,
&alt_names_lengths);
dns_names = crypto_cert_get_dns_names(cert->px509, &dns_names_count,
&dns_names_lengths);
/* compare against common name */
@ -1380,11 +1380,11 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
/* compare against alternative names */
if (alt_names)
if (dns_names)
{
for (index = 0; index < alt_names_count; index++)
for (index = 0; index < dns_names_count; index++)
{
if (tls_match_hostname(alt_names[index], alt_names_lengths[index], hostname))
if (tls_match_hostname(dns_names[index], dns_names_lengths[index], hostname))
{
hostname_match = TRUE;
break;
@ -1415,8 +1415,8 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
if (!hostname_match)
tls_print_certificate_name_mismatch_error(
hostname, port,
common_name, alt_names,
alt_names_count);
common_name, dns_names,
dns_names_count);
/* Automatically accept certificate on first use */
if (tls->settings->AutoAcceptCertificate)
@ -1508,9 +1508,9 @@ int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname,
certificate_data_free(certificate_data);
free(common_name);
if (alt_names)
crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths,
alt_names);
if (dns_names)
crypto_cert_dns_names_free(dns_names_count, dns_names_lengths,
dns_names);
if (verification_status > 0)
{