I’m kind of new to mbedTLS and I’m working on a custom web server.
I’m currently having trouble using mbedTLS to handle huge requests received from client & same for sending those kind of requests back to the client.
This is what I currently have (for server->client, haven’t yet tried client->server) : char* answer = malloc(sizeof(char)*DEFAULT_BUFLEN); static uint8_t buf[1024]; int len = strlen(answer);
for(int i = 0; i < len; i+= 500) { strncpy(buf, (answer+i), sizeof(unsigned char) * (i+500));
while( ( ret = mbedtls_ssl_write( &ssl, buf, sizeof(buf) ) ) <= 0 )`` {`` if( ret == MBEDTLS_ERR_NET_CONN_RESET ) { mbedtls_printf( " failed\n ! peer closed the connection\n\n" ); } if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret ); } }
answer is a char pointer allocated which will contain the string of the answer to send back to the client. (it’s filled in another function before that).
500 was chosen randomly because I tried a lot of different methods but none worked …
DEFAULT_BUFLEN = 1024
It crashes on the write when the length of answer is higher than the buffer buf, else it works, I’ve tried to check in which function it breaks but didn’t succeed (goes too far in the .c files).
Is that how I should handle huge requests ? Are there any other ways ? I’m kind of lost right now.
I am having trouble understanding what you are trying to achieve.
Are you trying to write 500 bytes each time?
There is a problem in the example you are showing: strncpy(buf, (answer+i), sizeof(unsigned char) * (i+500));
This, considering i is increased by 500 each iteration, if i is 1000 (smaller than answer length), you are trying to copy into buf (which length is 1024) i+500 bytes, which is 1500. This is an overflow of buf ( and of answer when it’s 1024 bytes as well).
In your example, I would use strncpy(buf, (answer+i), 500 ); , if I understand correctly your intentions.
But, why not use directly answer the the parameter to mbedtls_ssl_write()? Why is it allocated on the heap?
Yes I’ve looked into it a lot of times now but there are some things that I couldn’t understand on my own.
Yes I was basically stucked, my answer was too long (longer than the buffer buf) so when I just used the basic strcpy, I had the overflow error. So instead, because I want my code to work with whatever the size of answer is, I decided to copy each 500 bytes of answer into buf & send it until the end.
That’s right I didn’t think about that overflow error too but my point is : I thought I HAD TO use that buffer buf, it was mandatory in my head. I don’t know why.
I have no clue why I didn’t try without the buf .
Now my code is as mbedtls_ssl_write( &ssl, answer, strlen(answer) which “works”, at least I get an error : write 24704 which is CIPHER_FEATURE_UNAVAILABLE.
So I guess I have to add something to my config.h to handle “long requests” right ?
I found #define MBEDTLS_CIPHER_MODE_STREAM which I’m currently testing as I’m writing.
#define MBEDTLS_CIPHER_MODE_STREAM didn’t do the trick. I’m currently trying to DEBUG but I got a weird behaviour using my IDE Atollic TrueStudio 9.0.
When the programs calls mbedtls_gcm_crypt_and_tag( ctx->cipher_ctx, MBEDTLS_GCM_ENCRYPT, ilen, iv, iv_len, ad, ad_len, input, output, tag_len, tag ) from mbedtls_cipher_auth_encrypt, ctx->cipher_ctx is defined but when it gets into mbedtls_gcm_crypt_and_tag( mbedtls_gcm_context *ctx, ...) , ctx is not defined, or at least, I get strange / unreal variables into the members… Not sure if IDE problem or something else…
Are you sure you are using MBEDTLS_CIPHER_MODE_STREAM?
I suggest you look at the negotiated ciphersuite to understand what cipher is used.
Although the issue you are describing may be some IDE optimization, that you don’t see the trace, it also sounds to me that it could be some memory issue.
Note that mbedtls_ssl_write() receives as parameter the number of bytes you want to write from the buffer. Could you confirm that answer is a string literal ended with a nul character?
MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE is retrurned in the cipher module by several places, I suggest you insert your breakpoint in these locations to understand what feature is missing in your configuration.
No I don’t. I just gave it a try.
Alright Ill look into it.
Probably a memory issue yea.
I know, mbedtls_ssl_write is working fine for “small answers”. answer is a null char ended string. strl(len) works wonder on it.
I know exactly where it breaks : at the end of the function mbedtls_cipher_update of cipher.c because nothing is matched with the define and the cipher_mode in the function because mbedtls_cipher_context_t *ctx (the parameter) is completly broken → the int for example have random values, the mode of the cipher is at 165, …
Thank you for your clarification.
According to your description, since on “small answers” it works fine, then the only possible explanation is some memory overflow.
Have you tweaked to understand on what lengths the corruption starts to happen? What is the value of MBEDTLS_SSL_MAX_CONTENT_LEN in your platform? Is it the default 16KB? Have you checked return code of previous mbedtls_write_real() to check it has written all values?
Thanks for your answers.
I indeed had a memory overflow. (part of my answer was allocating 1024 bytes of memory).
MBEDTLS_SSL_MAX_CONTENT_LEN is actually 2048 but subject to change. mbedtls_write_real() works fine, gives me the actual length of answer.
But my concern is : how do I bypass those limitations (the 2048 bytes) because I still wanna handle answer>2048 . If I don’t want to increase that number may I loop into something to only send part of answer one by one ? What are the options that are offered to me.
The limitation of 2048 means your output buffer is 2048 bytes. If you want to send more than 2048, you will need to call mbedtls_write() with the amount of times you need. Since it returns the number of bytes written, the next call will be the length of the message, reduced by what was already sent.
If you want to send it in one time, you will need to increase MBEDTLS_SSL_MAX_CONTENT_LEN
I get it. But anyways if answer is 2450 bytes and I allocate 2048 by default I would have an overflow problem, I have to instead, as I build & add content to answer send it multiple times right ?
mbedtls_write is mbedtls_ssl_write right ?
Yes, I apologize for the typo
But anyways if answer is 2450 bytes and I allocate 2048 by default I would have an overflow problem, I have to instead, as I build & add content to answer send it multiple times right ?
Why would answer be 2450 bytes if you allocate only 2048? when do put information in answer?
If you mean that the MBEDTLS_SSL_MAX_CONTENT_LEN is 2048 bytes and the answer is 2450 bytes you will need to do something of the following, as shown in the ssl_client2 example:
for( written = 0, frags = 0; written < len; written += ret, frags++ )
{
while( ( ret = mbedtls_ssl_write( &ssl, buf + written,
len - written ) ) <= 0 )
{
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
mbedtls_printf( " failed\n ! mbedtls_ssl_write returned -0x%x\n\n",
-ret );
goto exit;
}
}
I’m basically allocating answer in my main.c (of length MBEDTLS_SSL_MAX_CONTENT_LEN )
When I read mbedtls_ssl_read( &ssl, buf, len ) a request (my work is a server), I place answer as a parameter : handle_request(answer, len, (char*)buf) because then in handle_request I can call a callback knowing what the user wants (method + location, for example GET + index.html) .
That callback will fill answer with different info . But sometimes I could put “too many” things in answer , more than the MBEDTLS_SSL_MAX_CONTENT_LEN size I mean.
So when I want to send the answer back while( ( ret = mbedtls_ssl_write( &ssl, answer, len ) ) <= 0 ), I get an error. (probably because I allocated only MBEDTLS_SSL_MAX_CONTENT_LEN for answer ) but the error actually is on : mbedtls_cipher_get_block_size( ctx ) because ctx->cipher_info is actually empty (same idea as before, in my other answer : post 7) …
I actually saw that example but couldn’t make it work. Imma try again.
Edit : ssl_server2 does exactly what I want but somehow it works even though buf is only 200 bytes while the HTTP_RESPONSE with LONG_RESPONSE enabled is 200+ bytes …
Edit 2 : nevermind ssl_server2 doesn’t work … it only sends 200 bytes. Rest of LONG_RESPONSE is ignored, or maybe I have to modify the value of IO_BUF_LEN to increase the size of buf ? Wouldn’t make sense in my opinion.