CryptoCert crypto_cert_read(uint8 * data, uint32 len) { CryptoCert cert = xmalloc(sizeof(*cert)); cert->data = ssl_cert_read(data, len); return cert; }
CRYPTO_CERT * crypto_cert_read(uint8 * data, uint32 len) { #ifdef CRYPTO_OPENSSL /* this will move the data pointer but we don't care, we don't use it again */ return d2i_X509(NULL, (D2I_X509_CONST unsigned char **) &data, len); #else /* built-in crypto */ return ssl_cert_read(data, len); #endif }
static inline void parse_mcs_data_sc_security(Stream & cr_stream, CryptContext & encrypt, CryptContext & decrypt, uint32_t & server_public_key_len, uint8_t (& client_crypt_random)[512], int & encryptionLevel, Random * gen) { LOG(LOG_INFO, "SC_SECURITY"); uint32_t encryptionMethod = cr_stream.in_uint32_le(); /* 1 = 40-bit, 2 = 128-bit */ LOG(LOG_INFO, "encryptionMethod = %u", encryptionMethod); encryptionLevel = cr_stream.in_uint32_le(); /* 1 = low, 2 = medium, 3 = high */ LOG(LOG_INFO, "encryptionLevel = %u", encryptionLevel); if (encryptionLevel == 0 && encryptionMethod == 0) { /* no encryption */ LOG(LOG_INFO, "No encryption"); return; } uint8_t modulus[SEC_MAX_MODULUS_SIZE]; uint8_t exponent[SEC_EXPONENT_SIZE]; memset(modulus, 0, sizeof(modulus)); memset(exponent, 0, sizeof(exponent)); ssllib ssl; // serverRandomLen (4 bytes): A 32-bit, unsigned integer. The size in bytes of // the serverRandom field. If the encryptionMethod and encryptionLevel fields // are both set to 0 then the contents of this field MUST be ignored and the // serverRandom field MUST NOT be present. Otherwise, this field MUST be set to // 32 bytes. uint32_t serverRandomLen = cr_stream.in_uint32_le(); LOG(LOG_INFO, "serverRandomLen = %u", serverRandomLen); if (serverRandomLen != SEC_RANDOM_SIZE) { LOG(LOG_ERR, "parse_crypt_info_error: serverRandomLen %d, expected %d", serverRandomLen, SEC_RANDOM_SIZE); throw Error(ERR_SEC_PARSE_CRYPT_INFO_BAD_RANDOM_LEN); } // serverCertLen (4 bytes): A 32-bit, unsigned integer. The size in bytes of the // serverCertificate field. If the encryptionMethod and encryptionLevel fields // are both set to 0 then the contents of this field MUST be ignored and the // serverCertificate field MUST NOT be present. uint32_t serverCertLen = cr_stream.in_uint32_le(); LOG(LOG_INFO, "serverCertLen = %u", serverCertLen); // serverRandom (variable): The variable-length server random value used to // derive session keys (see sections 5.3.4 and 5.3.5). The length in bytes is // given by the serverRandomLen field. If the encryptionMethod and // encryptionLevel fields are both set to 0 then this field MUST NOT be present. uint8_t serverRandom[SEC_RANDOM_SIZE] = {}; cr_stream.in_copy_bytes(serverRandom, serverRandomLen); // serverCertificate (variable): The variable-length certificate containing the // server's public key information. The length in bytes is given by the // serverCertLen field. If the encryptionMethod and encryptionLevel fields are // both set to 0 then this field MUST NOT be present. /* RSA info */ uint8_t * end = cr_stream.p + serverCertLen; if (end > cr_stream.end) { LOG(LOG_ERR, "serverCertLen outside of buffer (%u bytes, remains: %u)", serverCertLen, cr_stream.end - cr_stream.p); throw Error(ERR_SEC_PARSE_CRYPT_INFO_BAD_RSA_LEN); } uint32_t dwVersion = cr_stream.in_uint32_le(); /* 1 = RDP4-style, 0x80000002 = X.509 */ LOG(LOG_INFO, "dwVersion = %x", dwVersion); if (dwVersion & SCSecurityGccUserData::CERT_CHAIN_VERSION_1) { LOG(LOG_DEBUG, "We're going for the RDP4-style encryption"); // dwSigAlgId (4 bytes): A 32-bit, unsigned integer. The signature algorithm // identifier. This field MUST be set to SIGNATURE_ALG_RSA (0x00000001). uint32_t dwSigAlgId = cr_stream.in_uint32_le(); LOG(LOG_DEBUG, "dwSigAlgId = %u", dwSigAlgId); // dwKeyAlgId (4 bytes): A 32-bit, unsigned integer. The key algorithm // identifier. This field MUST be set to KEY_EXCHANGE_ALG_RSA (0x00000001). uint32_t dwKeyAlgId = cr_stream.in_uint32_le(); LOG(LOG_DEBUG, "dwKeyAlgId = %u", dwKeyAlgId); LOG(LOG_DEBUG, "ReceivingPublic key, RDP4-style"); // wPublicKeyBlobType (2 bytes): A 16-bit, unsigned integer. The type of data // in the PublicKeyBlob field. This field MUST be set to BB_RSA_KEY_BLOB // (0x0006). TODO("put assertion to check type and throw and error if not as expected"); uint16_t wPublicKeyBlobType = cr_stream.in_uint16_le(); LOG(LOG_DEBUG, "wPublicKeyBlobType = %u", wPublicKeyBlobType); // wPublicKeyBlobLen (2 bytes): A 16-bit, unsigned integer. The size in bytes // of the PublicKeyBlob field. uint16_t wPublicKeyBlobLen = cr_stream.in_uint16_le(); LOG(LOG_DEBUG, "wPublicKeyBlobLen = %u", wPublicKeyBlobLen); uint8_t * next_tag = cr_stream.p + wPublicKeyBlobLen; // PublicKeyBlob (variable): Variable-length server public key bytes, formatted // using the Rivest-Shamir-Adleman (RSA) Public Key structure (section // 2.2.1.4.3.1.1.1). The length in bytes is given by the wPublicKeyBlobLen // field. uint32_t magic = cr_stream.in_uint32_le(); if (magic != SEC_RSA_MAGIC) { LOG(LOG_WARNING, "RSA magic 0x%x", magic); throw Error(ERR_SEC_PARSE_PUB_KEY_MAGIC_NOT_OK); } server_public_key_len = cr_stream.in_uint32_le() - SEC_PADDING_SIZE; if ((server_public_key_len < SEC_MODULUS_SIZE) || (server_public_key_len > SEC_MAX_MODULUS_SIZE)) { LOG(LOG_WARNING, "Bad server public key size (%u bits)", server_public_key_len * 8); throw Error(ERR_SEC_PARSE_PUB_KEY_MODUL_NOT_OK); } cr_stream.in_skip_bytes(8); /* modulus_bits, unknown */ cr_stream.in_copy_bytes(exponent, SEC_EXPONENT_SIZE); cr_stream.in_copy_bytes(modulus, server_public_key_len); cr_stream.in_skip_bytes(SEC_PADDING_SIZE); LOG(LOG_DEBUG, "Got Public key, RDP4-style"); // This should not be necessary as previous field if fully decoded cr_stream.p = next_tag; LOG(LOG_DEBUG, "Receiving key sig RDP4-style"); // wSignatureBlobType (2 bytes): A 16-bit, unsigned integer. The type of data // in the SignatureKeyBlob field. This field is set to BB_RSA_SIGNATURE_BLOB // (0x0008). TODO("put assertion to check type and throw and error if not as expected"); uint16_t wSignatureBlobType = cr_stream.in_uint16_le(); LOG(LOG_DEBUG, "wSignatureBlobType = %u", wSignatureBlobType); // wSignatureBlobLen (2 bytes): A 16-bit, unsigned integer. The size in bytes // of the SignatureKeyBlob field. uint16_t wSignatureBlobLen = cr_stream.in_uint16_le(); // SignatureBlob (variable): Variable-length signature of the certificate // created with the Terminal Services Signing Key (see sections 5.3.3.1.1 and // 5.3.3.1.2). The length in bytes is given by the wSignatureBlobLen field. cr_stream.in_skip_bytes(wSignatureBlobLen); LOG(LOG_DEBUG, "Got key sig RDP4-style"); } else { LOG(LOG_DEBUG, "We're going for the RDP5-style encryption"); uint32_t certcount = cr_stream.in_uint32_le(); LOG(LOG_DEBUG, "Certcount = %u", certcount); if (certcount < 2){ LOG(LOG_DEBUG, "Server didn't send enough X509 certificates"); throw Error(ERR_SEC_PARSE_CRYPT_INFO_CERT_NOK); } for (; certcount > 2; certcount--){ /* ignore all the certificates between the root and the signing CA */ LOG(LOG_WARNING, " Ignored certs left: %d", certcount); uint32_t ignorelen = cr_stream.in_uint32_le(); LOG(LOG_WARNING, "Ignored Certificate length is %d", ignorelen); SSL_CERT *ignorecert = ssl_cert_read(cr_stream.p, ignorelen); cr_stream.in_skip_bytes(ignorelen); if (ignorecert == NULL){ LOG(LOG_WARNING, "got a bad cert: this will probably screw up" " the rest of the communication"); } LOG(LOG_WARNING, "cert #%d (ignored)", certcount); } /* Do da funky X.509 stuffy "How did I find out about this? I looked up and saw a bright light and when I came to I had a scar on my forehead and knew about X.500" - Peter Gutman in a early version of http://www.cs.auckland.ac.nz/~pgut001/pubs/x509guide.txt */ /* Loading CA_Certificate from server*/ uint32_t cacert_len = cr_stream.in_uint32_le(); LOG(LOG_DEBUG, "CA Certificate length is %d", cacert_len); SSL_CERT *cacert = ssl_cert_read(cr_stream.p, cacert_len); cr_stream.in_skip_bytes(cacert_len); if (NULL == cacert){ LOG(LOG_DEBUG, "Couldn't load CA Certificate from server"); throw Error(ERR_SEC_PARSE_CRYPT_INFO_CACERT_NULL); } /* Loading Certificate from server*/ uint32_t cert_len = cr_stream.in_uint32_le(); LOG(LOG_DEBUG, "Certificate length is %d", cert_len); SSL_CERT *server_cert = ssl_cert_read(cr_stream.p, cert_len); cr_stream.in_skip_bytes(cert_len); if (NULL == server_cert){ ssl_cert_free(cacert); LOG(LOG_DEBUG, "Couldn't load Certificate from server"); throw Error(ERR_SEC_PARSE_CRYPT_INFO_CACERT_NOT_LOADED); } /* Matching certificates */ if (!ssl_certs_ok(server_cert, cacert)){ ssl_cert_free(server_cert); ssl_cert_free(cacert); LOG(LOG_DEBUG, "Security error CA Certificate invalid"); throw Error(ERR_SEC_PARSE_CRYPT_INFO_CACERT_NOT_MATCH); } ssl_cert_free(cacert); cr_stream.in_skip_bytes(16); /* Padding */ SSL_RKEY *server_public_key = ssl_cert_to_rkey(server_cert, server_public_key_len); LOG(LOG_DEBUG, "Server public key length=%u", (unsigned)server_public_key_len); if (NULL == server_public_key){ LOG(LOG_DEBUG, "Didn't parse X509 correctly"); ssl_cert_free(server_cert); throw Error(ERR_SEC_PARSE_CRYPT_INFO_X509_NOT_PARSED); } ssl_cert_free(server_cert); LOG(LOG_INFO, "server_public_key_len=%d, MODULUS_SIZE=%d MAX_MODULUS_SIZE=%d", server_public_key_len, SEC_MODULUS_SIZE, SEC_MAX_MODULUS_SIZE); if ((server_public_key_len < SEC_MODULUS_SIZE) || (server_public_key_len > SEC_MAX_MODULUS_SIZE)){ LOG(LOG_DEBUG, "Bad server public key size (%u bits)", server_public_key_len * 8); ssl.rkey_free(server_public_key); throw Error(ERR_SEC_PARSE_CRYPT_INFO_MOD_SIZE_NOT_OK); } if (ssl_rkey_get_exp_mod(server_public_key, exponent, SEC_EXPONENT_SIZE, modulus, SEC_MAX_MODULUS_SIZE) != 0){ LOG(LOG_DEBUG, "Problem extracting RSA exponent, modulus"); ssl.rkey_free(server_public_key); throw Error(ERR_SEC_PARSE_CRYPT_INFO_RSA_EXP_NOT_OK); } ssl.rkey_free(server_public_key); TODO(" find a way to correctly dispose of garbage at end of buffer") /* There's some garbage here we don't care about */ } uint8_t client_random[SEC_RANDOM_SIZE]; memset(client_random, 0, sizeof(SEC_RANDOM_SIZE)); /* Generate a client random, and determine encryption keys */ gen->random(client_random, SEC_RANDOM_SIZE); ssl.rsa_encrypt(client_crypt_random, client_random, SEC_RANDOM_SIZE, server_public_key_len, modulus, exponent); rdp_sec_generate_keys(encrypt, decrypt, encrypt.sign_key, client_random, serverRandom, encryptionMethod); }