diff --git a/include/freerdp/crypto/crypto.h b/include/freerdp/crypto/crypto.h index aeda47ec8..199fdfd5f 100644 --- a/include/freerdp/crypto/crypto.h +++ b/include/freerdp/crypto/crypto.h @@ -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,41 @@ 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_name); 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); 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 */ diff --git a/libfreerdp/crypto/crypto.c b/libfreerdp/crypto/crypto.c index 8564c573d..b949fd700 100644 --- a/libfreerdp/crypto/crypto.c +++ b/libfreerdp/crypto/crypto.c @@ -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,452 @@ 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); +} + +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); +} + +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; +} + + +void crypto_cert_dns_names_free(int count, int* lengths, + char** dns_names) +{ free(lengths); - if (alt_name) + if (dns_names) { - for (i=0; itype == GEN_DNS) - { - length = ASN1_STRING_to_UTF8(&string, subject_alt_name->d.dNSName); - strings[*count] = (char*) string; - (*lengths)[*count] = length; - (*count)++; - } + if (!result || !(*lengths)) + { + free(result); + free(*lengths); + (*lengths) = 0; + (*count) = 0; + return result; } - if (*count < 1) + for (i = 0; i < list.count; i ++) { - free(strings) ; - free(*lengths) ; - *lengths = NULL ; - return NULL; + result[i] = list.strings[i]; + (*lengths)[i] = strlen(result[i]); } -out: - GENERAL_NAMES_free(subject_alt_names); - - return strings; + string_list_free(&list); + return result; } + char* crypto_cert_issuer(X509* xcert) { return crypto_print_name(X509_get_issuer_name(xcert)); @@ -382,7 +753,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 +762,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 +797,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 +807,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 +826,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 +840,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); diff --git a/libfreerdp/crypto/test/CMakeLists.txt b/libfreerdp/crypto/test/CMakeLists.txt index 80b91195e..3df0619ae 100644 --- a/libfreerdp/crypto/test/CMakeLists.txt +++ b/libfreerdp/crypto/test/CMakeLists.txt @@ -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}") diff --git a/libfreerdp/crypto/test/Test_x509_cert_info.c b/libfreerdp/crypto/test/Test_x509_cert_info.c new file mode 100644 index 000000000..53b83605a --- /dev/null +++ b/libfreerdp/crypto/test/Test_x509_cert_info.c @@ -0,0 +1,140 @@ +#include + + +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 = "Test_x509_cert_info.pem"; +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)); +} + diff --git a/libfreerdp/crypto/test/Test_x509_cert_info.pem b/libfreerdp/crypto/test/Test_x509_cert_info.pem new file mode 100644 index 000000000..5ac118709 --- /dev/null +++ b/libfreerdp/crypto/test/Test_x509_cert_info.pem @@ -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----- diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index 5af8e4e72..0f260410c 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -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) {