mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
* RSA_decrypt now checks the integrity of the first 11 bytes.
* The size of the output buffer in RSA_decrypt is now checked and cleared. * get_random now returns an error code * Various system calls now check the return code to remove gcc warnings. git-svn-id: svn://svn.code.sf.net/p/axtls/code/trunk@237 9a5d90b5-6617-0410-8a86-bb477d3ed2e3
This commit is contained in:
parent
08b27ee1cb
commit
9ef84f9234
@ -203,7 +203,7 @@ void RSA_pub_key_new(RSA_CTX **rsa_ctx,
|
|||||||
const uint8_t *pub_exp, int pub_len);
|
const uint8_t *pub_exp, int pub_len);
|
||||||
void RSA_free(RSA_CTX *ctx);
|
void RSA_free(RSA_CTX *ctx);
|
||||||
int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
|
int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data,
|
||||||
int is_decryption);
|
int out_len, int is_decryption);
|
||||||
bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg);
|
bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg);
|
||||||
#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT)
|
#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT)
|
||||||
bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
|
bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len,
|
||||||
@ -220,8 +220,8 @@ void RSA_print(const RSA_CTX *ctx);
|
|||||||
EXP_FUNC void STDCALL RNG_initialize(void);
|
EXP_FUNC void STDCALL RNG_initialize(void);
|
||||||
EXP_FUNC void STDCALL RNG_custom_init(const uint8_t *seed_buf, int size);
|
EXP_FUNC void STDCALL RNG_custom_init(const uint8_t *seed_buf, int size);
|
||||||
EXP_FUNC void STDCALL RNG_terminate(void);
|
EXP_FUNC void STDCALL RNG_terminate(void);
|
||||||
EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data);
|
EXP_FUNC int STDCALL get_random(int num_rand_bytes, uint8_t *rand_data);
|
||||||
void get_random_NZ(int num_rand_bytes, uint8_t *rand_data);
|
int get_random_NZ(int num_rand_bytes, uint8_t *rand_data);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -156,11 +156,12 @@ EXP_FUNC void STDCALL RNG_terminate(void)
|
|||||||
/**
|
/**
|
||||||
* Set a series of bytes with a random number. Individual bytes can be 0
|
* Set a series of bytes with a random number. Individual bytes can be 0
|
||||||
*/
|
*/
|
||||||
EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data)
|
EXP_FUNC int STDCALL get_random(int num_rand_bytes, uint8_t *rand_data)
|
||||||
{
|
{
|
||||||
#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
|
#if !defined(WIN32) && defined(CONFIG_USE_DEV_URANDOM)
|
||||||
/* use the Linux default */
|
/* use the Linux default - read from /dev/urandom */
|
||||||
read(rng_fd, rand_data, num_rand_bytes); /* read from /dev/urandom */
|
if (read(rng_fd, rand_data, num_rand_bytes) < 0)
|
||||||
|
return -1;
|
||||||
#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB)
|
#elif defined(WIN32) && defined(CONFIG_WIN32_USE_CRYPTO_LIB)
|
||||||
/* use Microsoft Crypto Libraries */
|
/* use Microsoft Crypto Libraries */
|
||||||
CryptGenRandom(gCryptProv, num_rand_bytes, rand_data);
|
CryptGenRandom(gCryptProv, num_rand_bytes, rand_data);
|
||||||
@ -198,21 +199,25 @@ EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data)
|
|||||||
/* insert the digest at the start of the entropy pool */
|
/* insert the digest at the start of the entropy pool */
|
||||||
memcpy(entropy_pool, digest, MD5_SIZE);
|
memcpy(entropy_pool, digest, MD5_SIZE);
|
||||||
#endif
|
#endif
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a series of bytes with a random number. Individual bytes are not zero.
|
* Set a series of bytes with a random number. Individual bytes are not zero.
|
||||||
*/
|
*/
|
||||||
void get_random_NZ(int num_rand_bytes, uint8_t *rand_data)
|
int get_random_NZ(int num_rand_bytes, uint8_t *rand_data)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
get_random(num_rand_bytes, rand_data);
|
if (get_random(num_rand_bytes, rand_data))
|
||||||
|
return -1;
|
||||||
|
|
||||||
for (i = 0; i < num_rand_bytes; i++)
|
for (i = 0; i < num_rand_bytes; i++)
|
||||||
{
|
{
|
||||||
while (rand_data[i] == 0) /* can't be 0 */
|
while (rand_data[i] == 0) /* can't be 0 */
|
||||||
rand_data[i] = (uint8_t)(rand());
|
rand_data[i] = (uint8_t)(rand());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
43
crypto/rsa.c
43
crypto/rsa.c
@ -134,21 +134,26 @@ void RSA_free(RSA_CTX *rsa_ctx)
|
|||||||
/**
|
/**
|
||||||
* @brief Use PKCS1.5 for decryption/verification.
|
* @brief Use PKCS1.5 for decryption/verification.
|
||||||
* @param ctx [in] The context
|
* @param ctx [in] The context
|
||||||
* @param in_data [in] The data to encrypt (must be < modulus size-11)
|
* @param in_data [in] The data to decrypt (must be < modulus size-11)
|
||||||
* @param out_data [out] The encrypted data.
|
* @param out_data [out] The decrypted data.
|
||||||
|
* @param out_len [int] The size of the decrypted buffer in bytes
|
||||||
* @param is_decryption [in] Decryption or verify operation.
|
* @param is_decryption [in] Decryption or verify operation.
|
||||||
* @return The number of bytes that were originally encrypted. -1 on error.
|
* @return The number of bytes that were originally encrypted. -1 on error.
|
||||||
* @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
|
* @see http://www.rsasecurity.com/rsalabs/node.asp?id=2125
|
||||||
*/
|
*/
|
||||||
int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data,
|
int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data,
|
||||||
uint8_t *out_data, int is_decryption)
|
uint8_t *out_data, int out_len, int is_decryption)
|
||||||
{
|
{
|
||||||
const int byte_size = ctx->num_octets;
|
const int byte_size = ctx->num_octets;
|
||||||
int i, size;
|
int i = 0, size;
|
||||||
bigint *decrypted_bi, *dat_bi;
|
bigint *decrypted_bi, *dat_bi;
|
||||||
uint8_t *block = (uint8_t *)alloca(byte_size);
|
uint8_t *block = (uint8_t *)alloca(byte_size);
|
||||||
|
int pad_count = 0;
|
||||||
|
|
||||||
memset(out_data, 0, byte_size); /* initialise */
|
if (out_len < byte_size) /* check output has enough size */
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(out_data, 0, out_len); /* initialise */
|
||||||
|
|
||||||
/* decrypt */
|
/* decrypt */
|
||||||
dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size);
|
dat_bi = bi_import(ctx->bi_ctx, in_data, byte_size);
|
||||||
@ -162,28 +167,37 @@ int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data,
|
|||||||
/* convert to a normal block */
|
/* convert to a normal block */
|
||||||
bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size);
|
bi_export(ctx->bi_ctx, decrypted_bi, block, byte_size);
|
||||||
|
|
||||||
i = 10; /* start at the first possible non-padded byte */
|
if (block[i++] != 0) /* leading 0? */
|
||||||
|
return -1;
|
||||||
|
|
||||||
#ifdef CONFIG_SSL_CERT_VERIFICATION
|
#ifdef CONFIG_SSL_CERT_VERIFICATION
|
||||||
if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */
|
if (is_decryption == 0) /* PKCS1.5 signing pads with "0xff"s */
|
||||||
{
|
{
|
||||||
while (block[i++] == 0xff && i < byte_size);
|
if (block[i++] != 0x01) /* BT correct? */
|
||||||
|
return -1;
|
||||||
|
|
||||||
if (block[i-2] != 0xff)
|
while (block[i++] == 0xff && i < byte_size)
|
||||||
i = byte_size; /*ensure size is 0 */
|
pad_count++;
|
||||||
}
|
}
|
||||||
else /* PKCS1.5 encryption padding is random */
|
else /* PKCS1.5 encryption padding is random */
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
while (block[i++] && i < byte_size);
|
if (block[i++] != 0x02) /* BT correct? */
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (block[i++] && i < byte_size)
|
||||||
|
pad_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* check separator byte - and padding must be 8 or more bytes */
|
||||||
|
if (i == byte_size || pad_count < 8)
|
||||||
|
return -1;
|
||||||
|
|
||||||
size = byte_size - i;
|
size = byte_size - i;
|
||||||
|
|
||||||
/* get only the bit we want */
|
/* get only the bit we want */
|
||||||
if (size > 0)
|
|
||||||
memcpy(out_data, &block[i], size);
|
memcpy(out_data, &block[i], size);
|
||||||
|
return size;
|
||||||
return size ? size : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -249,7 +263,8 @@ int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len,
|
|||||||
else /* randomize the encryption padding with non-zero bytes */
|
else /* randomize the encryption padding with non-zero bytes */
|
||||||
{
|
{
|
||||||
out_data[1] = 2;
|
out_data[1] = 2;
|
||||||
get_random_NZ(num_pads_needed, &out_data[2]);
|
if (get_random_NZ(num_pads_needed, &out_data[2]) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_data[2+num_pads_needed] = 0;
|
out_data[2+num_pads_needed] = 0;
|
||||||
|
15
httpd/proc.c
15
httpd/proc.c
@ -188,7 +188,12 @@ static void procdirlisting(struct connstruct *cn)
|
|||||||
{
|
{
|
||||||
snprintf(buf, sizeof(buf), HTTP_VERSION
|
snprintf(buf, sizeof(buf), HTTP_VERSION
|
||||||
" 200 OK\nContent-Type: text/html\n\n");
|
" 200 OK\nContent-Type: text/html\n\n");
|
||||||
write(cn->networkdesc, buf, strlen(buf));
|
if (write(cn->networkdesc, buf, strlen(buf)) < 0)
|
||||||
|
{
|
||||||
|
printf("procdirlisting: could not write");
|
||||||
|
TTY_FLUSH();
|
||||||
|
}
|
||||||
|
|
||||||
removeconnection(cn);
|
removeconnection(cn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -625,7 +630,13 @@ static void proccgi(struct connstruct *cn)
|
|||||||
/* Send POST query data to CGI script */
|
/* Send POST query data to CGI script */
|
||||||
if ((cn->reqtype == TYPE_POST) && (cn->content_length > 0))
|
if ((cn->reqtype == TYPE_POST) && (cn->content_length > 0))
|
||||||
{
|
{
|
||||||
write(spipe[1], cn->post_data, cn->content_length);
|
if (write(spipe[1], cn->post_data, cn->content_length) == -1)
|
||||||
|
{
|
||||||
|
printf("[CGI]: could write to pipe");
|
||||||
|
TTY_FLUSH();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
close(spipe[0]);
|
close(spipe[0]);
|
||||||
close(spipe[1]);
|
close(spipe[1]);
|
||||||
|
|
||||||
|
@ -57,6 +57,9 @@
|
|||||||
//#define DEFAULT_CLNT_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES
|
//#define DEFAULT_CLNT_OPTION SSL_DISPLAY_BYTES|SSL_DISPLAY_STATES
|
||||||
#define DEFAULT_CLNT_OPTION 0
|
#define DEFAULT_CLNT_OPTION 0
|
||||||
|
|
||||||
|
/* hack to remove gcc warning */
|
||||||
|
#define SYSTEM(A) if (system(A) < 0) printf("system call error\n");
|
||||||
|
|
||||||
static int g_port = 19001;
|
static int g_port = 19001;
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
@ -545,7 +548,7 @@ static int RSA_test(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
RSA_encrypt(rsa_ctx, (const uint8_t *)"abc", 3, enc_data2, 0);
|
RSA_encrypt(rsa_ctx, (const uint8_t *)"abc", 3, enc_data2, 0);
|
||||||
RSA_decrypt(rsa_ctx, enc_data2, dec_data2, 1);
|
RSA_decrypt(rsa_ctx, enc_data2, dec_data2, sizeof(dec_data2), 1);
|
||||||
if (memcmp("abc", dec_data2, 3))
|
if (memcmp("abc", dec_data2, 3))
|
||||||
{
|
{
|
||||||
printf("Error: ENCRYPT/DECRYPT #2 failed\n");
|
printf("Error: ENCRYPT/DECRYPT #2 failed\n");
|
||||||
@ -823,7 +826,7 @@ static void do_client(client_t *clnt)
|
|||||||
g_port, clnt->openssl_option);
|
g_port, clnt->openssl_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
system(openssl_buf);
|
SYSTEM(openssl_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SSL_server_test(
|
static int SSL_server_test(
|
||||||
@ -1326,7 +1329,7 @@ static void do_server(server_t *svr)
|
|||||||
"-accept %d -quiet %s ", g_port, svr->openssl_option);
|
"-accept %d -quiet %s ", g_port, svr->openssl_option);
|
||||||
}
|
}
|
||||||
|
|
||||||
system(openssl_buf);
|
SYSTEM(openssl_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int SSL_client_test(
|
static int SSL_client_test(
|
||||||
@ -1646,8 +1649,8 @@ cleanup:
|
|||||||
{
|
{
|
||||||
ssl_display_error(ret);
|
ssl_display_error(ret);
|
||||||
printf("Error: A client test failed\n");
|
printf("Error: A client test failed\n");
|
||||||
system("sh ../ssl/test/killopenssl.sh");
|
SYSTEM("sh ../ssl/test/killopenssl.sh");
|
||||||
system("sh ../ssl/test/killgnutls.sh");
|
SYSTEM("sh ../ssl/test/killgnutls.sh");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2065,7 +2068,7 @@ static void do_header_issue(void)
|
|||||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
||||||
#endif
|
#endif
|
||||||
sprintf(axtls_buf, "./axssl s_client -connect localhost:%d", g_port);
|
sprintf(axtls_buf, "./axssl s_client -connect localhost:%d", g_port);
|
||||||
system(axtls_buf);
|
SYSTEM(axtls_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int header_issue(void)
|
static int header_issue(void)
|
||||||
@ -2099,7 +2102,12 @@ static int header_issue(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size = fread(buf, 1, sizeof(buf), f);
|
size = fread(buf, 1, sizeof(buf), f);
|
||||||
SOCKET_WRITE(client_fd, buf, size);
|
if (SOCKET_WRITE(client_fd, buf, size) < 0)
|
||||||
|
{
|
||||||
|
ret = SSL_ERROR_SOCK_SETUP_FAILURE;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
usleep(200000);
|
usleep(200000);
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
@ -2108,7 +2116,7 @@ error:
|
|||||||
SOCKET_CLOSE(client_fd);
|
SOCKET_CLOSE(client_fd);
|
||||||
SOCKET_CLOSE(server_fd);
|
SOCKET_CLOSE(server_fd);
|
||||||
TTY_FLUSH();
|
TTY_FLUSH();
|
||||||
system("killall axssl");
|
SYSTEM("killall axssl");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2208,29 +2216,29 @@ int main(int argc, char *argv[])
|
|||||||
if (SSL_basic_test())
|
if (SSL_basic_test())
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
system("sh ../ssl/test/killopenssl.sh");
|
SYSTEM("sh ../ssl/test/killopenssl.sh");
|
||||||
|
|
||||||
if (SSL_unblocked_test())
|
if (SSL_unblocked_test())
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
system("sh ../ssl/test/killopenssl.sh");
|
SYSTEM("sh ../ssl/test/killopenssl.sh");
|
||||||
|
|
||||||
if (SSL_client_tests())
|
if (SSL_client_tests())
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
system("sh ../ssl/test/killopenssl.sh");
|
SYSTEM("sh ../ssl/test/killopenssl.sh");
|
||||||
system("sh ../ssl/test/killgnutls.sh");
|
SYSTEM("sh ../ssl/test/killgnutls.sh");
|
||||||
|
|
||||||
if (SSL_server_tests())
|
if (SSL_server_tests())
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
system("sh ../ssl/test/killopenssl.sh");
|
SYSTEM("sh ../ssl/test/killopenssl.sh");
|
||||||
|
|
||||||
//if (header_issue())
|
if (header_issue())
|
||||||
//{
|
{
|
||||||
// printf("Header tests failed\n"); TTY_FLUSH();
|
printf("Header tests failed\n"); TTY_FLUSH();
|
||||||
// goto cleanup;
|
goto cleanup;
|
||||||
//}
|
}
|
||||||
|
|
||||||
ret = 0; /* all ok */
|
ret = 0; /* all ok */
|
||||||
printf("**** ALL TESTS PASSED ****\n"); TTY_FLUSH();
|
printf("**** ALL TESTS PASSED ****\n"); TTY_FLUSH();
|
||||||
|
@ -310,7 +310,9 @@ static int send_server_hello(SSL *ssl)
|
|||||||
buf[5] = ssl->version & 0x0f;
|
buf[5] = ssl->version & 0x0f;
|
||||||
|
|
||||||
/* server random value */
|
/* server random value */
|
||||||
get_random(SSL_RANDOM_SIZE, &buf[6]);
|
if (get_random(SSL_RANDOM_SIZE, &buf[6]) < 0)
|
||||||
|
return SSL_NOT_OK;
|
||||||
|
|
||||||
memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE);
|
memcpy(ssl->dc->server_random, &buf[6], SSL_RANDOM_SIZE);
|
||||||
offset = 6 + SSL_RANDOM_SIZE;
|
offset = 6 + SSL_RANDOM_SIZE;
|
||||||
|
|
||||||
@ -391,7 +393,8 @@ static int process_client_key_xchg(SSL *ssl)
|
|||||||
|
|
||||||
/* rsa_ctx->bi_ctx is not thread-safe */
|
/* rsa_ctx->bi_ctx is not thread-safe */
|
||||||
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
|
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
|
||||||
premaster_size = RSA_decrypt(rsa_ctx, &buf[offset], premaster_secret, 1);
|
premaster_size = RSA_decrypt(rsa_ctx, &buf[offset], premaster_secret,
|
||||||
|
sizeof(premaster_secret), 1);
|
||||||
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
|
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
|
||||||
|
|
||||||
if (premaster_size != SSL_SECRET_SIZE ||
|
if (premaster_size != SSL_SECRET_SIZE ||
|
||||||
@ -400,7 +403,9 @@ static int process_client_key_xchg(SSL *ssl)
|
|||||||
premaster_secret[1] != (ssl->client_version & 0x0f))
|
premaster_secret[1] != (ssl->client_version & 0x0f))
|
||||||
{
|
{
|
||||||
/* guard against a Bleichenbacher attack */
|
/* guard against a Bleichenbacher attack */
|
||||||
get_random(SSL_SECRET_SIZE, premaster_secret);
|
if (get_random(SSL_SECRET_SIZE, premaster_secret) < 0)
|
||||||
|
return SSL_NOT_OK;
|
||||||
|
|
||||||
/* and continue - will die eventually when checking the mac */
|
/* and continue - will die eventually when checking the mac */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,7 +458,7 @@ static int process_cert_verify(SSL *ssl)
|
|||||||
|
|
||||||
/* rsa_ctx->bi_ctx is not thread-safe */
|
/* rsa_ctx->bi_ctx is not thread-safe */
|
||||||
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
|
SSL_CTX_LOCK(ssl->ssl_ctx->mutex);
|
||||||
n = RSA_decrypt(x509_ctx->rsa_ctx, &buf[6], dgst_buf, 0);
|
n = RSA_decrypt(x509_ctx->rsa_ctx, &buf[6], dgst_buf, sizeof(dgst_buf), 0);
|
||||||
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
|
SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex);
|
||||||
|
|
||||||
if (n != SHA1_SIZE + MD5_SIZE)
|
if (n != SHA1_SIZE + MD5_SIZE)
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user