Beispiel #1
0
CryptoCert
crypto_cert_read(uint8 * data, uint32 len)
{
	CryptoCert cert = xmalloc(sizeof(*cert));
	cert->data = ssl_cert_read(data, len);
	return cert;
}
Beispiel #2
0
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
}
Beispiel #3
0
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);
}