Reading/writing encrypted data using file descriptors with OpenSSL - c

I'm not sure if this is a common way of interfacing with OpenSSL or not, but I can't really find any examples or references to trying to do it this way.
I have some applications that interfact with a number of things using file descriptors (nothing fancy here). This works great for plain text TCP sockets, for example: you can just read/write using normal standard library functions. I've used SSL_write and SSL_read before to read/write from encrypted connections, but I find this very clunky. The objective is handling I/O for things that could either be encrypted or not, and handling them in the same way. With the above, you have to pass around both SSL *ssl for encrypted connections and then a normal file descriptor for unencrypted connections, and depending on whether it's encrypted or not, use the right I/O functions, not all of which even have an SSL equivalent.
For example, the following macros illustrate this:
#ifdef HAVE_SSL_RECV
#define my_recv(ssl, fd, buf, len, flags) (ssl ? SSL_recv(ssl, buf, len, flags) : recv(fd, buf, len, flags)
#endif
#define my_read(ssl, fd, buf, len) (ssl ? SSL_read(ssl, buf, len) : read(fd, buf, len))
#define my_write(ssl, fd, buf, len) (ssl ? SSL_write(ssl, buf, len) : write(fd, buf, len))
What I would like to do is just have a file descriptor referring to the decrypted side of the SSL connection, such that anything written to or read from that file descriptor is encrypted/decrypted as you would expect with using SSL_read and SSL_write. What I'm on unclear on is if there is any builtin or standard way of doing this.
This related question (kind of but not quite) suggested BIO pairs: OpenSSL: perform en-/decryption without SSL_read() / SSL_write()
However, reading the man page for those (https://www.openssl.org/docs/manmaster/man3/BIO_s_bio.html), I don't think this is quite what I want, since BIO pairs do buffering and that's not what I need at all here.
Most similar types of interfaces that I've used provide a way to get a file descriptor that you can use in your program, and OpenSSL does have SSL_get_wfd and SSL_get_rfd, which I assumed would be what I was looking for, but it seems these are actually the raw socket file descriptor itself, exactly the same thing as the file descriptor passed to SSL_set_fd in the first place! (Confirmed by printing out all 3 file descriptors). So I'm not even sure what the point of these functions are in the first place, they don't provide any new information.
Obviously, I could use do this manually, by creating a pipe and then using one end of the pipe as my file descriptor for reading/writing and then spawn another thread to read/write from it and relay to the appropriate SSL functions, appropriately abstracting away that the connection is using TLS. But, is there a better or more standard way of doing this, and a way to avoid the overhead of additional threads for each SSL connection? (If not, that's fine, but I wanted to be sure before I went with that approach.)

Related

SO_TIMESTAMP with OpenSSL?

When using a non-encrypted socket I'd use
int num bytes = recvmsg(sock, &msg, 0)
and then get the SO_TIMESTAMP info from the msg (see e.g. Linux recvmsg() not getting a software timestamp from socket and https://linux.die.net/man/2/recvmsg).
However, there doesn't seem to be a corresponding API for an SSL-encrypted socket, at least for OpenSSL. The only one I can see is
int SSL_read(SSL *ssl, void *buf, int num);
which obviously doesn't propagate the timestamp info.
Have people tried this before? I can see a few options,
fork/extend OpenSSL at the point where it reads from the raw socket and carry the data across
do a recvmsg(s, data, flags) explicitly and somehow pass that into an OpenSSL function for subsequent decoding.
use a different library?
I find surprisingly little info about this online.
Thanks!
Followed the comments suggestion by Steffen Ullrich (thank you) to make something work.
I ended up implementing a custom BIO along the lines of https://github.com/openssl/openssl/blob/master/crypto/bio/bio_sock.c but with recvmsg for the read path, and grabbing the SO_TIMESTAMP, which I subsequently stick in custom data.
I attach the BIO (SSL_set_bio) to my SSL object, and can retrieve the timestamp after a succesful SSL_read.
It was quite painful.

Socket programming problem: Is the new socket fd used only once?

I want to write basic chat program. I don't release that, I just want to learn socket programming. The chat program will be between client and server.
server code:
//bla bla code
new_socket = accept(server_fd, (struct sockaddr*)&address,(socklen_t*)&addrlen);
char server_msg[128];
char client_msg[128];
int running = 1;
while(running){
fgets(server_msg, 64, stdin);
send(new_socket, server_msg, strlen(server_msg), 0);
recv(new_socket, client_msg, 128, 0);
}
client code:
char server_msg[128];
char client_msg[128];
int running = 1;
while(running){
fgets(client_msg, 64, stdin);
send(server_socket, client_msg, strlen(client_msg), 0);
recv(server_socket, server_msg, 128, 0);
}
Questions:
Is the new socket fd used only once? That means; Will I do create a new socket for each sending and receiving. Or can I use this forever?
If first question answer is "FOREVER", Will I do something to new socket fd? I don't know maybe clear.
The above code is not working as expected. As I expected. Actually, The code is working very well :D. How do I perform interrupt operations such as getting input, sending messages, receiving messages?
My English is not good, I'm sorry.
Is the new socket fd used only once? That means; Will I do create a new socket for each sending and receiving. Or can I use this forever?
you open a TCP socket connection, so, once in the connected state (the ESTABLISHED state) it remains there until one side closes it (and the other side will notice it by reading 0 bytes as result, this explanation is oversimplified to make it simpler to your case, normally you should detect the end of file in the socket, so you can close it, and accept(2) another connection)
If first question answer is "FOREVER", Will I do something to new socket fd? I don't know maybe clear.
Your program is not a chat program, as the server uses the SAME socket to write what it has read, so it will return the message sent to the same client that issued it (acting more as an echo server, than a chat)
The above code is not working as expected. As I expected. Actually, The code is working very well :D. How do I perform interrupt operations such as getting input, sending messages, receiving messages?
Normally, a chat server should wait for a message to arrive at several sockets (by means of the select(2) system call) and will determine which socket needs attention. As a result, the server will read(2) from that socket (probably in a non blocking way, to avoid being blocked on a read to a socket, that will not be interrupted if data enters on another socket) The chat server should write what it has read over all the other sockets it has connections from, so every other user of your chat system receives a copy of the message (sending back the message to the sender is optional, but not very elegant, the issuer of the message should do local echo of it, although, to ensure the user sees his messages interspersed between the ones from other users)
I recommend you to read the book "Unix Network Programming" from Richard Stevens. It describes perfectly how to use the sockets API and IMHO it is the best reference you can get to learn how to use the socket interface.
One final note:
Don't use plain numbers hardwired in your code, as it is error prone (you declare a buffer string to be 128 bytes, but then read only 64, making 64 unused bytes in the array, you can use sizeof buffer as to make the compiler to use the amount declared in the variable, or you can #define BUFFSIZE 128 and then declare char buffer[BUFFSIZE]; and later do a fgets(buffer, BUFFSIZE, socket_FILE_descriptor); if you use a constant to size a buffer, use a constant name to name it :) and use it everywhere, in case you decide to give it a different value, all the occurences of it will change, instead of having to reviste all places in your code where you have used the number 64.

Why does POSIX not define a mid-layer socket API?

I am looking at socket programming again. I get the details (well, I can copy them from various websites, and I know the code is enabling the Unix low-level procedures), but I don't get the POSIX logic and thinking in its API.
Why have they not defined a slightly higher-level interface built on these lower-level socket functions?
Presumably, such code could factor out code that is repeated often (and error-prone) into more convenient FILE like interfaces. Factoring would seem even more appropriate than just convenient when the lower level use is the same in > 90% of its use. Almost all sockets use that I see in application programs open a socket, read and write to it and close the socket. Also, why does one need to bind, when this is really something that the open call always does?
What cases does the current interface even cover that could not easily be covered by an interface that would look almost like the FILE interface?
One explanation is that there are uses where one would not bind to a socket, for example, or where fgets/fputs/fprintf/fscanf like functionality would need something extra (time-outs)?
There must be a reason that I am missing. Otherwise, 20 years later, there would already be one or more standard libraries that facilitate this and that would be in wide use. I couldn't find one on google that mimics all the FILE routines.
The point is strikingly simple:
Because sockets are not files.
Let me elaborate: recv/send works quite like read/write, if you limit yourself to linearly reading a file from the beginning, and to appending at its end.
However, you'll say, send doesn't let me write arbitrary lengths of data trough! If I try to send more data than fits into a protocol's packet buffer, it will throw an error!
And that's actually the beauty of sockets: you actually send the data away. you can't keep it; it's gone once it's sent, and it's not stored once it's received. Sockets give you a whole different set of abilities (like sending smaller packets than the maximum packet size of the network, for example), which on the other hand demand you take some control yourself.
EDIT: send will not "throw" an error. "throwing" is not a C/Posix way of handling errors. Instead it will return an error (from man 2 send):
If the message is too long to pass atomically through the underlying protocol, the error EMSGSIZE is returned, and the message is not transmitted.
The C programming language is and will likely always be a lightweight one. You need to understand that C runs basically anywhere and some things need a long research and work to get standardized.
Also, I have seen that new libraries are added because C++ went ahead and made them standard so it's a kind of C sharing.
Please do note that you can "bind" a socket to a file through fdopen(3) and consider it as a binary file. Of course you will still need to bind it, make it listen, accept and all the actions you can do on a socket that won't work for a file.
Indeed, despite the similar interface, a socket acts only partially as a UNIX file: there's even an errno value, ENOTSOCK which indicates a socket specific operation on a non-socket file descriptor.
Furthermore, consider buffering. You do want a file write to be done in large chunks, thus a bigger buffering, to make it faster; this won't work for a socket as you need to send data immediately, that is, undelayed.
Consider this example:
char one = '1', two = '2', three = '3';
fwrite(&one, 1, 1, socket_file);
fprintf(socket_file, "%c\n", two);
send(fd, &three, 1, 0);
where fd is a connected socket(AF_INET, SOCK_STREAM, 0) and socket_file = fdopen(fd, "w+"). The receiver will read 312 because there's no flush except upon process termination at the FILE layer, unlike with send where three is sent immediately.

TCP client failed to send string to server

I am programming TCP server client. I sending the three string seperately using seperate send system call.
But receiving end i getting only single string that is first string which i send. remaining two string missed.
Below i given the part of my server client program.
client.c
char *info = "infolog";
char *size = "filesize";
char *end = "fileend";
send(client, info, strlen(info)+1, 0);
send(client, size, strlen(size)+1, 0);
send(client, end, strlen(end)+1, 0);
server.c
while ((read_size = recv(client, msg, sizeof(msg), 0))) {
printf("Data: %s\n", msg);
memset(msg, 0, sizeof(msg));
}
Actual output:
Data: infolog
Expected output
Data: infolog
Data: filesize
Data: fileend
Thanks.
Try printing out read_size. You probably have received all the messages already.
Due to Nagle's Algorithm, the sender probably batched up your three send() calls and sent a single packet to the server. While you can disable Nagle's algorithm, I don't think it's a good idea in this case. Your server needs to be able to handle receiving of partial data, and handle receiving more data than it expects.
You might want to look into using an upper-layer protocol for your messages, such as Google Protocol Buffers. Take a look at the techniques page, where they describe how they might do it: build up a protocol buffer, and write its length to the stream before writing the buffer itself. That way the receive side can read the length and then determine how many bytes it needs to read before it has a complete message.
TCP is not a message protocol but a byte stream protocol.
The three send-s could be recv-ed as a single input (or something else, e.g. in two or five recv etc....)
The application should analyze the input and buffer it to be able to splice it in meaningful messages.
the transmission may split or merge the messages, e.g. intermediate routers can and will split or merge the "packets".
In practice you'll better have some good conventions about your messages. Either decide that each message is e.g. newline terminated, or decide that it starts with some header giving its size.
Look at HTTP, SMTP, IMAP, SCGI or ONC/XDR (documented in RFC5531) as concrete examples. And document quite well your protocol (a minima, in long descriptive comments for some homework toy project, and more seriously, in a separate public document).

Send large files over socket in C

I am writing a simple FTP server for a school project. Project is almost done, the only problem I do have is sending a file over socket to a client. I can't write to a socket more than 200kb of data, small files are downloaded succesfully. Can anyone tell me what is the correct way of sending large files over Linux sockets ?
Thanks in advance.
PS I am using C and 32-bit linux, server working in PORT mode, been using low level open,write,read and other functions such as sendfile,send,sendto.
you can mmap the file and write it on the socket from there, you can also get its size using fstat, like this:
fd = open(filename, O_RDONLY);
struct stat s;
fstat(fd, &s); // i get the size
adr = mmap(NULL, s.st_size, PROT_READ, MAP_SHARED, fd, 0); // i get the adress
write(socket, adr, s.st_size); // i send the file from this adress directly
You may want to send just the size of the file before you send it entirely. Your client maybe want to send you that he got the good size and that he can manage to download it.
One idea might be reading the file chunk by chunk, something like:
Pseudo-code
#define CHUNK_SIZE 1000
void send(){
uint8_t buff[CHUNK_SIZE];
int actually_read;
while((actually_read = read(fd, buff, sizeof(buff)) > 0)
sendto(sock_fd, buff, actually_read, 0);
}
You should add some error checking, but the idea is to read a considerable amount of bytes from the file you want to send and send that amount. In the server side you need to do a similar thing, by reading from the socket the arriving chunks and writing them to a file. You might want to prefix some metadata to buff just to tell the server which file you're transmitting if you want to handle multiple file transfers. Since FTP uses TCP you shouldn't worry about losing data.
Again, this is just an idea. There are multiple ways of doing this I suppose.

Resources