SSLv3 mutual authentication using libcurl and smart card - libcurl

I need some help to establish a communication channel to consume a webservice using SSLv3 with mutual authentication, libcurl and a smart card, which will store the client certificate, the key pair and will be responsible for signatures, encryptions, etc.
For testing purposes, without using the smart card, the following brief code runs smoothly:
....
(SOAP message configurations)
....
curl_easy_setopt (curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt (curl, CURLOPT_SSLVERSION, 3);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
curl_easy_setopt (curl, CURLOPT_SSLCERT, "clientCertificateFile");
curl_easy_setopt (curl, CURLOPT_KEYPASSWD, "testPrivateKeyPass");
curl_easy_setopt (curl, CURLOPT_SSLKEY, "testPrivateKeyFile");
curl_easy_setopt (curl, CURLOPT_CAINFO, "CAcertificateFile");
curl_easy_setopt (curl, CURLOPT_URL, "URLtoWebService");
curl_easy_perform (curl);
After running this code, I can establish communication using SSLv3 and get the expected response from the webservice. However, I need to establish this secure channel using the smart card. I have free access to the following information:
"clientCertificateFile" - stored on the smart card, in PEM format. It can be read through access function.
"CAcertificateFile" - stored on disk, in PEM format. It can be read through access function.
"URLtoWebService" - stored in the application.
"testPrivateKeyFile" and "testPrivateKeyPass" are temporary files used only for testing and should be replaced by the smart card.
My doubt is: How to use the smart card to replace the private key and password in the libcurl setup process?
The smart card does not allow access to private key, only to the public key, as expected. I can send to the smart card data to be signed or encrypted, using the private key, and get the resulting buffer.
Is there any way to redirect part of the SSLv3 connection handshake to use my smart card? Some setting in lib ssl, or libcurl?
Thanks for listening!

Related

Slow Performance with libcurl when changing Ip Address

First time developing with libcurl and got some unexpected results:
I'm trying to reuse the same CURL handle in order to reuse it's connections associated with it. However, I noticed a huge performance drop when I constantly change the Ip address of my request.
When I keep the IP address unchanged everything is fine, I'm pretty sure there's something I'm missing, but I don't know what and where to search for.
Here's my code:
// curl init
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RestClientPool::WriteCallBack); // custom write function
curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 200L);
// perform request, curl handle is stored and reused
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_URL, req_url.c_str()); // here req_url is something like http://123.123.123.123:8080/api/api
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
CURLcode res = curl_easy_perform(curl);
Problem:
say i have 3 hosts with ip: a, b and c, if i send requests to a,b,c,a,b,c,a... 100 times V.S. a,a,a,a,a,a... 100 times with the same handle, performance is around 9s to 1s (service on all hosts are identical, no host issues needs to be considered)
PS: I can tell that libcurl handle is reusing the connections by monitoring(and by verbose), because i monitored the servers. So, new connections when ip change shouldn't be the problem here.
Environment:
libcurl:
curl 7.86.0 (x86_64-pc-linux-gnu) libcurl/7.86.0 OpenSSL/1.0.2k-fips zlib/1.2.7
Release-Date: 2022-10-26
Protocols: dict file ftp ftps gophergophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HSTS HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets
os:
CentOS 7.6.1810
What I tried:
1、I trie using CURLOPT_RESOLVE(cause i thought it might have something do with DNS cache), but nothing changed.
2、I made three handle and fired off 3 threads, where each handle would request to same ip(host A for example) repeatedly(no handle were shared across threads). And it worked fine.
However, when I change the ip for each handle(thread 1 would now repeatedly make request to host A, thread 2 to B and thread 3 to C), the performance drop appears again.

C++ libcurl: curl_easy_perform() returned CURLE_OK eventhough it received response code 226

I'm sending files from linux server to windows remote system using libcurl FTP.
Below is the code
curl_easy_setopt(CurlSessionHandle, CURLOPT_URL, remoteFileUrl);
curl_easy_setopt(CurlSessionHandle, CURLOPT_UPLOAD, ON);
// Set the input local file handle
curl_easy_setopt(CurlSessionHandle, CURLOPT_READDATA, localFileHandle);
// Set on/off all wanted options
// Enable ftp data connection
curl_easy_setopt(CurlSessionHandle, CURLOPT_NOBODY, OFF);
// Create missing directory into FTP path
curl_easy_setopt(CurlSessionHandle, CURLOPT_FTP_CREATE_MISSING_DIRS , ON) ;
// Set the progress function, in order to check the stop transfer request
curl_easy_setopt(CurlSessionHandle, CURLOPT_NOPROGRESS, OFF);
curl_easy_setopt(CurlSessionHandle, CURLOPT_PROGRESSFUNCTION, progressCb);
curl_easy_setopt(CurlSessionHandle, CURLOPT_PROGRESSDATA, this);
CURLcode Result = curl_easy_perform(CurlSessionHandle);
====================================================================
few files are not transferred to remote but i didn't receive any error from curl_easy_perform(). This is happening randomly.
I have collected the wireshark logs, trace shows [RST, ACK] was sent from our side to remote system don't know the reason and response code 226 was sent from remote system, i think i should receive some error code from curl_easy_perform()
instead of CURLE_OK. Please correct me if i'm wrong.
Please check the image which has wireshark traces.
Source IP: 82 is Linux server & Destination IP: 87 is Windows remote system
I would like to know why we are sending [RST, ACK] to remote and why libcurl is not returning error code. Can someone explain to me is there a way to handle this problem.
I have uploaded images of success and failure case. Please check and let me know
Failure case, Check the response arg

How to know if a server is online or not, if we only know the IP?

How can that be done, the server is not an HTTP server, its a ArmA game server.
I tried to achieve it using CURL in the following code, but it didn't work, it always show Offline.
IsOnline( "xx.xxx.xx.xxx" );
bool IsOnline( string url )
{
CURL *curl;
curl = curl_easy_init();
if(curl)
{
curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );
CURLcode result = curl_easy_perform(curl);
if ( result != CURLE_OK )
{
error_string = curl_easy_strerror( result );
return false;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return true;
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return false;
}
What you're doing in your code is trying to send an HTTP request. The reason it fails is because the game server machine isn't running a HTTP server.
The usual way to find out whether a machine is online or not is by pinging it. Ping uses the ICMP protocol. Here is a tutorial explaining how to ping from a C++ program (it's Windows-specific): http://www.developerfusion.com/article/4628/how-to-ping/
However, the admins of the server might have disabled ICMP, in which case the machine won't respond to pings even if it's online. Moreover, you can get "false positives" if the machine is online and responding to pings, but the game server software itself isn't running.
So I think your best bet would be to find out which ports the game server itself listens to and attempt a connection on those ports.

How to make a TCP socket work with SO_BINDTODEVICE (against routing table)

Background of the question:
On our machine we have multiple network interfaces which lead to different networks. There are possible overlapping IP addresses (e.g. two different machines in two different networks with the same IP address). So when we want to connect with specific peer then we need to specify not only it's IP address but also our network interface which lead to the proper network.
We want to write application in C/C++ able to connect with specific peers via TCP.
Question:
I'm trying to make a TCP connection using socket with SO_BINDTODEVICE set. Here is a simplified snippet:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, interface_name,
strlen(interface_name));
connect(sockfd, (sockaddr *) &serv_addr, sizeof(serv_addr));
(I know about struct ifreq, but it seems that only the first field in it (ifr_name field is in use). So I can pass only name of the interface.)
If forced interface is the same as interface according to the routing table, then everything works correctly.
If forced interface is different, then (tested with Wireshark):
SYN is sent from forced interface to desired peer.
SYN,ACK from desired peer is received on forced interface.
ACK is not sent from forced interface and connection is not established. (And goto step 2.)
How to check where SYN,ACK or ACK is rejected by our system? And how correctly force TCP socket to make connection using specific interface (against routing table)?
Maybe there are some other, more convenient ways to create TCP connection on desired interface?
Thanks!
I know it wouldn't be your quite answer, but you could disable other interfaces and just enable the network you want, in your case it seems that you need all the interfaces, but I think this approach could help others. you could enable/disable network interface with something like this :
enable
ifr.ifr_flags = true;
strcpy(ifr.ifr_name, "eth0"); //you could put any interface name beside "eth0"
res = ioctl(sockfd, SIOCSIFFLAGS, &ifr);
and for disable you just need to set flag to false and the rest of the code is the same :
ifr.ifr_flags = true;
Don't use SO_BINDTODEVICE. Its not supported on all platforms and there's an easier way.
Instead bind the socket to the local IP address on the correct network that you want to use to connect to the remote side.
Ie,
sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = 0; //Auto-determine port.
sin.sin_addr.s_addr = inet_addr("192.168.1.123"); //Your IP address on same network as peer you want to connect to
bind(sockfd, (sockaddr*)&sin, sizeof(sin));
Then call connect.
For the server side you'd do the same thing except specify a port instead of 0, then call listen instead of connect.
This is a problem with kernel configuration - on many distributions it is by default configured to reject incoming packets in this specific case.
I found the solution to this problem in this answer to another similar question:
To allow such traffic you have to set some variables on your machine (as root):
sysctl -w net.ipv4.conf.all.accept_local=1
sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.your_nic.rp_filter=0
where your_nic is the network interface receiving the packet. Beware to change both net.ipv4.conf.all.rp_filter and net.ipv4.conf.your_nic.rp_filter, it will not work otherwise (the kernel defaults to the most restrictive setting).

Does setting CURLOPT_URL force to create a second FTP connection?

I want to open a connection with an FTP server and download 2 different files. Names are totally different and I cannot use wildcards.
I expected I could set the hostname and the file, then call curl_easy_perform, then set the file again and call curl_easy_perform one last time.
However it seems I have to use the CURLOPT_URL which includes both the hostname and the filename.
My fear is that the following code (lacks the error checking just to be short here):
...
curl_easy_setopt(handle, CURLOPT_URL, "ftp://myserver//foo.dat");
curl_easy_perform(handle);
curl_easy_setopt(handle, CURLOPT_URL, "ftp://myserver//bar.png");
curl_easy_perform(handle);
opens the FTP connection twice, giving a lot of avoidable overhead.
So am I missing something here? Will libcurl notice that the hostname part is the same, thus avoiding to open the same connection twice? If not how can I open the connection only once?
Enabling CURLOPT_VERSBOSE showed that:
* Connection #0 to host 127.0.0.1 left intact
* Re-using existing connection! (#0) with host 127.0.0.1
* Connected to 127.0.0.1 (127.0.0.1) port 21 (#0)
* Request has same path as previous transfer
Also, wireshark showed that connection to port 21 is made only once and lasts throughout the whole transfer (including the two files).
However one connection per-file is made on another port because of the ftp passive mode, but I think this is not curl's fault.