⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 secure.c

📁 ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机理和API函数调用几乎相同。甚至可以兼容XP的程序。喜欢研究系统内核的人可以看一看。
💻 C
📖 第 1 页 / 共 2 页
字号:
		return False;
	}

	in_uint8s(s, 8);	/* modulus_bits, unknown */
	in_uint8p(s, *exponent, SEC_EXPONENT_SIZE);
	in_uint8p(s, *modulus, modulus_len);
	in_uint8s(s, SEC_PADDING_SIZE);
	This->secure.server_public_key_len = modulus_len;

	return s_check(s);
}

static BOOL
sec_parse_x509_key(RDPCLIENT * This, X509 * cert)
{
	EVP_PKEY *epk = NULL;
	/* By some reason, Microsoft sets the OID of the Public RSA key to
	   the oid for "MD5 with RSA Encryption" instead of "RSA Encryption"

	   Kudos to Richard Levitte for the following (. intiutive .)
	   lines of code that resets the OID and let's us extract the key. */
	if (OBJ_obj2nid(cert->cert_info->key->algor->algorithm) == NID_md5WithRSAEncryption)
	{
		DEBUG_RDP5(("Re-setting algorithm type to RSA in server certificate\n"));
		ASN1_OBJECT_free(cert->cert_info->key->algor->algorithm);
		cert->cert_info->key->algor->algorithm = OBJ_nid2obj(NID_rsaEncryption);
	}
	epk = X509_get_pubkey(cert);
	if (NULL == epk)
	{
		error("Failed to extract public key from certificate\n");
		return False;
	}

	This->secure.server_public_key = RSAPublicKey_dup((RSA *) epk->pkey.ptr);
	EVP_PKEY_free(epk);

	This->secure.server_public_key_len = RSA_size(This->secure.server_public_key);
	if ((This->secure.server_public_key_len < 64) || (This->secure.server_public_key_len > SEC_MAX_MODULUS_SIZE))
	{
		error("Bad server public key size (%u bits)\n", This->secure.server_public_key_len * 8);
		return False;
	}

	return True;
}


/* Parse a crypto information structure */
static BOOL
sec_parse_crypt_info(RDPCLIENT * This, STREAM s, uint32 * rc4_key_size,
		     uint8 ** server_random, uint8 ** modulus, uint8 ** exponent)
{
	uint32 crypt_level, random_len, rsa_info_len;
	uint32 cacert_len, cert_len, flags;
	X509 *cacert, *server_cert;
	uint16 tag, length;
	uint8 *next_tag, *end;

	in_uint32_le(s, *rc4_key_size);	/* 1 = 40-bit, 2 = 128-bit */
	in_uint32_le(s, crypt_level);	/* 1 = low, 2 = medium, 3 = high */
	if (crypt_level == 0)	/* no encryption */
		return False;
	in_uint32_le(s, random_len);
	in_uint32_le(s, rsa_info_len);

	if (random_len != SEC_RANDOM_SIZE)
	{
		error("random len %d, expected %d\n", random_len, SEC_RANDOM_SIZE);
		return False;
	}

	in_uint8p(s, *server_random, random_len);

	/* RSA info */
	end = s->p + rsa_info_len;
	if (end > s->end)
		return False;

	in_uint32_le(s, flags);	/* 1 = RDP4-style, 0x80000002 = X.509 */
	if (flags & 1)
	{
		DEBUG_RDP5(("We're going for the RDP4-style encryption\n"));
		in_uint8s(s, 8);	/* unknown */

		while (s->p < end)
		{
			in_uint16_le(s, tag);
			in_uint16_le(s, length);

			next_tag = s->p + length;

			switch (tag)
			{
				case SEC_TAG_PUBKEY:
					if (!sec_parse_public_key(This, s, modulus, exponent))
						return False;
					DEBUG_RDP5(("Got Public key, RDP4-style\n"));

					break;

				case SEC_TAG_KEYSIG:
					/* Is this a Microsoft key that we just got? */
					/* Care factor: zero! */
					/* Actually, it would probably be a good idea to check if the public key is signed with this key, and then store this
					   key as a known key of the hostname. This would prevent some MITM-attacks. */
					break;

				default:
					unimpl("crypt tag 0x%x\n", tag);
			}

			s->p = next_tag;
		}
	}
	else
	{
		uint32 certcount;

		DEBUG_RDP5(("We're going for the RDP5-style encryption\n"));
		in_uint32_le(s, certcount);	/* Number of certificates */

		if (certcount < 2)
		{
			error("Server didn't send enough X509 certificates\n");
			This->disconnect_reason = 1798;
			return False;
		}

		for (; certcount > 2; certcount--)
		{		/* ignore all the certificates between the root and the signing CA */
			uint32 ignorelen;
			X509 *ignorecert;

			DEBUG_RDP5(("Ignored certs left: %d\n", certcount));

			in_uint32_le(s, ignorelen);
			DEBUG_RDP5(("Ignored Certificate length is %d\n", ignorelen));
			ignorecert = d2i_X509(NULL, &(s->p), ignorelen);

			if (ignorecert == NULL)
			{	/* XXX: error out? */
				DEBUG_RDP5(("got a bad cert: this will probably screw up the rest of the communication\n"));
			}

#ifdef WITH_DEBUG_RDP5
			DEBUG_RDP5(("cert #%d (ignored):\n", certcount));
			X509_print_fp(stdout, ignorecert);
#endif
		}

		/* 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
		 */

		in_uint32_le(s, cacert_len);
		DEBUG_RDP5(("CA Certificate length is %d\n", cacert_len));
		cacert = d2i_X509(NULL, &(s->p), cacert_len);
		/* Note: We don't need to move s->p here - d2i_X509 is
		   "kind" enough to do it for us */
		if (NULL == cacert)
		{
			error("Couldn't load CA Certificate from server\n");
			This->disconnect_reason = 1798;
			return False;
		}

		/* Currently, we don't use the CA Certificate.
		   FIXME:
		   *) Verify the server certificate (server_cert) with the
		   CA certificate.
		   *) Store the CA Certificate with the hostname of the
		   server we are connecting to as key, and compare it
		   when we connect the next time, in order to prevent
		   MITM-attacks.
		 */

		X509_free(cacert);

		in_uint32_le(s, cert_len);
		DEBUG_RDP5(("Certificate length is %d\n", cert_len));
		server_cert = d2i_X509(NULL, &(s->p), cert_len);
		if (NULL == server_cert)
		{
			error("Couldn't load Certificate from server\n");
			This->disconnect_reason = 1798;
			return False;
		}

		in_uint8s(s, 16);	/* Padding */

		/* Note: Verifying the server certificate must be done here,
		   before sec_parse_public_key since we'll have to apply
		   serious violence to the key after this */

		if (!sec_parse_x509_key(This, server_cert))
		{
			DEBUG_RDP5(("Didn't parse X509 correctly\n"));
			X509_free(server_cert);
			This->disconnect_reason = 1798;
			return False;
		}
		X509_free(server_cert);
		return True;	/* There's some garbage here we don't care about */
	}
	return s_check_end(s);
}

/* Process crypto information blob */
static void
sec_process_crypt_info(RDPCLIENT * This, STREAM s)
{
	uint8 *server_random, *modulus, *exponent;
	uint8 client_random[SEC_RANDOM_SIZE];
	uint32 rc4_key_size;

	if (!sec_parse_crypt_info(This, s, &rc4_key_size, &server_random, &modulus, &exponent))
	{
		DEBUG(("Failed to parse crypt info\n"));
		return;
	}

	DEBUG(("Generating client random\n"));
	generate_random(client_random);

	if (NULL != This->secure.server_public_key)
	{			/* Which means we should use
				   RDP5-style encryption */
		uint8 inr[SEC_MAX_MODULUS_SIZE];
		uint32 padding_len = This->secure.server_public_key_len - SEC_RANDOM_SIZE;

		/* This is what the MS client do: */
		memset(inr, 0, padding_len);
		/*  *ARIGL!* Plaintext attack, anyone?
		   I tried doing:
		   generate_random(inr);
		   ..but that generates connection errors now and then (yes,
		   "now and then". Something like 0 to 3 attempts needed before a
		   successful connection. Nice. Not!
		 */
		memcpy(inr + padding_len, client_random, SEC_RANDOM_SIZE);
		reverse(inr + padding_len, SEC_RANDOM_SIZE);

		RSA_public_encrypt(This->secure.server_public_key_len,
				   inr, This->secure.crypted_random, This->secure.server_public_key, RSA_NO_PADDING);

		reverse(This->secure.crypted_random, This->secure.server_public_key_len);

		RSA_free(This->secure.server_public_key);
		This->secure.server_public_key = NULL;
	}
	else
	{			/* RDP4-style encryption */
		sec_rsa_encrypt(This->secure.crypted_random,
				client_random, SEC_RANDOM_SIZE, This->secure.server_public_key_len, modulus,
				exponent);
	}
	sec_generate_keys(This, client_random, server_random, rc4_key_size);
}


/* Process SRV_INFO, find RDP version supported by server */
static void
sec_process_srv_info(RDPCLIENT * This, STREAM s)
{
	in_uint16_le(s, This->server_rdp_version);
	DEBUG_RDP5(("Server RDP version is %d\n", This->server_rdp_version));
	if (1 == This->server_rdp_version)
	{
		This->use_rdp5 = 0;
		This->server_depth = 8;
	}
}


/* Process connect response data blob */
void
sec_process_mcs_data(RDPCLIENT * This, STREAM s)
{
	uint16 tag, length;
	uint8 *next_tag;
	uint8 len;

	in_uint8s(s, 21);	/* header (T.124 ConferenceCreateResponse) */
	in_uint8(s, len);
	if (len & 0x80)
		in_uint8(s, len);

	while (s->p < s->end)
	{
		in_uint16_le(s, tag);
		in_uint16_le(s, length);

		if (length <= 4)
			return;

		next_tag = s->p + length - 4;

		switch (tag)
		{
			case SEC_TAG_SRV_INFO:
				sec_process_srv_info(This, s);
				break;

			case SEC_TAG_SRV_CRYPT:
				sec_process_crypt_info(This, s);
				break;

			case SEC_TAG_SRV_CHANNELS:
				/* FIXME: We should parse this information and
				   use it to map RDP5 channels to MCS
				   channels */
				break;

			default:
				unimpl("response tag 0x%x\n", tag);
		}

		s->p = next_tag;
	}
}

/* Receive secure transport packet */
STREAM
sec_recv(RDPCLIENT * This, uint8 * rdpver)
{
	uint32 sec_flags;
	uint16 channel;
	STREAM s;

	while ((s = mcs_recv(This, &channel, rdpver)) != NULL)
	{
		if (rdpver != NULL)
		{
			if (*rdpver != 3)
			{
				if (*rdpver & 0x80)
				{
					in_uint8s(s, 8);	/* signature */
					sec_decrypt(This, s->p, (int)(s->end - s->p));
				}
				return s;
			}
		}
		if (This->encryption || !This->licence_issued)
		{
			in_uint32_le(s, sec_flags);

			if (sec_flags & SEC_ENCRYPT)
			{
				in_uint8s(s, 8);	/* signature */
				sec_decrypt(This, s->p, (int)(s->end - s->p));
			}

			if (sec_flags & SEC_LICENCE_NEG)
			{
				licence_process(This, s);
				continue;
			}

			if (sec_flags & 0x0400)	/* SEC_REDIRECT_ENCRYPT */
			{
				uint8 swapbyte;

				in_uint8s(s, 8);	/* signature */
				sec_decrypt(This, s->p, (int)(s->end - s->p));

				/* Check for a redirect packet, starts with 00 04 */
				if (s->p[0] == 0 && s->p[1] == 4)
				{
					/* for some reason the PDU and the length seem to be swapped.
					   This isn't good, but we're going to do a byte for byte
					   swap.  So the first foure value appear as: 00 04 XX YY,
					   where XX YY is the little endian length. We're going to
					   use 04 00 as the PDU type, so after our swap this will look
					   like: XX YY 04 00 */
					swapbyte = s->p[0];
					s->p[0] = s->p[2];
					s->p[2] = swapbyte;

					swapbyte = s->p[1];
					s->p[1] = s->p[3];
					s->p[3] = swapbyte;

					swapbyte = s->p[2];
					s->p[2] = s->p[3];
					s->p[3] = swapbyte;
				}
#ifdef WITH_DEBUG
				/* warning!  this debug statement will show passwords in the clear! */
				hexdump(s->p, s->end - s->p);
#endif
			}

		}

		if (channel != MCS_GLOBAL_CHANNEL)
		{
			channel_process(This, s, channel);
			*rdpver = 0xff;
			return s;
		}

		return s;
	}

	return NULL;
}

/* Establish a secure connection */
BOOL
sec_connect(RDPCLIENT * This, char *server, wchar_t *hostname, char *cookie)
{
	struct stream mcs_data;
	void * p = malloc(512);

	if(p == NULL)
	{
		This->disconnect_reason = 262;
		return False;
	}

	/* We exchange some RDP data during the MCS-Connect */
	mcs_data.size = 512;
	mcs_data.p = mcs_data.data = (uint8 *) p;
	sec_out_mcs_data(This, &mcs_data, hostname);

	if (!mcs_connect(This, server, cookie, &mcs_data))
		return False;

	/*      sec_process_mcs_data(&mcs_data); */
	if (This->encryption)
		sec_establish_key(This);
	free(mcs_data.data);
	return True;
}

/* Establish a secure connection */
BOOL
sec_reconnect(RDPCLIENT * This, char *server, wchar_t *hostname, char *cookie)
{
	struct stream mcs_data;
	void * p = malloc(512);

	if(p == NULL)
	{
		This->disconnect_reason = 262;
		return False;
	}

	/* We exchange some RDP data during the MCS-Connect */
	mcs_data.size = 512;
	mcs_data.p = mcs_data.data = (uint8 *) p;
	sec_out_mcs_data(This, &mcs_data, hostname);

	if (!mcs_reconnect(This, server, cookie, &mcs_data))
		return False;

	/*      sec_process_mcs_data(&mcs_data); */
	if (This->encryption)
		sec_establish_key(This);
	free(mcs_data.data);
	return True;
}

/* Disconnect a connection */
void
sec_disconnect(RDPCLIENT * This)
{
	mcs_disconnect(This);
}

/* reset the state of the sec layer */
void
sec_reset_state(RDPCLIENT * This)
{
	This->server_rdp_version = 0;
	This->secure.encrypt_use_count = 0;
	This->secure.decrypt_use_count = 0;
	mcs_reset_state(This);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -