I'm developing a webservice using gSOAP with SSL support. It works fine as long as I COPY (that's it!) the code provided as gSOAP documentation. Trying to add some feature I collide with a lot of difficulties! I don't have a good knowledge of OpenSSL libraries, so I'm here to ask your help.
I should add a CRL list to check the certificate sent by the client. How can I do it? Moreover, I changed the function pointed by soap.fsslverify with this one:
int servlet_fsslverify(int ok, X509_STORE_CTX *store)
{
ok = 1;
char buf[1024];
X509 *cert = X509_STORE_CTX_get_current_cert(store);
fprintf(stderr, "SSL verify error or warning with certificate at depth %d: %s\n",
X509_STORE_CTX_get_error_depth(store),
X509_verify_cert_error_string(X509_STORE_CTX_get_error(store)));
X509_NAME_oneline(X509_get_issuer_name(cert), buf, sizeof(buf));
fprintf(stderr, "certificate issuer %s\n", buf);
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
fprintf(stderr, "certificate subject %s\n", buf);
/* Note: return 1 to continue, but unsafe progress will be terminated by OpenSSL */
return ok;
}
It is called everytime a client tries to authenticate. As you can see, I'm able to check the fields in client's certificate, but I really don't know how to check if the particular certificate is present or not in a CRL.
That's all, thanks a lot to everyone would be so nice to answer.
I found the solution on the internet! Sorry I don't remember where, but I'll try to find it back and I'll edit this post - credits to the solver.
Code to be inserted right after server-context creation.
X509_STORE *store;
store = SSL_CTX_get_cert_store(ServicePtr->ctx);
if (store)
{
X509_LOOKUP *lookup;
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK);
lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
if (lookup)
{
if ( X509_load_crl_file(lookup, globals.pki_crl, X509_FILETYPE_ASN1) < 1 )
{
std::cerr << "CRL not found or invalid" << std::endl;
}
}
else
{
std::cerr << "Unable to create a valid lookup" << std::endl;
}
}
else
{
std::cerr << "Unable to get X509_STORE" << std::endl;
}
Related
I'm trying to make a basic Client-Server program that can exchange messages using the OpenSSL library. I'm very new to OpenSSL and cryptography and I'm trying to understand exactly how to make sure that the connection between my client and server is secure. Currently I'm using self signed certificates for both client and server but the certificate verification fails when the client tries to connect to the server with this error:
140336190395008:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed:../ssl/statem/statem_srvr.c:3711:
In the main method of my server program, I first set verify for the CTX and use the following flags:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
Then when setting up the servlet I call the following method to show certificates:
void ShowCerts(SSL* ssl) /*show the ceritficates to client and match them*/ {
X509 *cert;
long int verify;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
verify = SSL_get_verify_result(ssl);
if (verify == X509_V_OK) {
printf("Yay it worked\n");
}
if ( cert != NULL ) {
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Server: %s\n", line); /*server certifcates*/
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("client: %s\n", line); /*client certificates*/
free(line);
X509_free(cert);
} else {
printf("No certificates.\n");
}
}
As I said I'm very new to this so I might be missing something basic here. Also apologies in advance if I missed any important info, this is my first time asking a question here.
Edit: Upon Checking on IPv6 Test I Found out My ISP does not provide IPv6... otherwise this code is good
getaddrinfo() always fails for IPv6 with the error code 11268096, but it is successful for IPv4.
Setting Hint.ai_family = AF_INET6; is what triggers the error, but I do not know why.
Also, how do I get the sin_port/sin6_port in numbers? I am always getting port 0.(As #Remy Lebeau pointed out I am only asking for IP of the Domain so it won't output port...)
void GetAddrInfoFromHostNameIPV6(const char* DomainName, addrinfo* Result, bool& IsSuccessful)
{
IsSuccessful = false;
addrinfo Hint;
addrinfo* Return = nullptr;
int ErrorCode;
memset(&Hint, 0, sizeof(Hint));
Hint.ai_family = AF_INET6;
Hint.ai_socktype = SOCK_STREAM;
//Hint.ai_socktype = SOCK_DGRAM;
ErrorCode = getaddrinfo(DomainName, NULL, &Hint, &Return) << '\n';
if (ErrorCode != 0)
{
std::cout << "\n Error GetAddrInfoFromHostNameIPV6() Failed with Error Code: " << ErrorCode << " in GetAddrInfoFromHostName In: NW_P!";
}
else
{
*Result = *Return;
char IpAddress[INET6_ADDRSTRLEN];
uint16_t Port;
inet_ntop(AF_INET6, &((sockaddr_in6*)((Result)->ai_addr))->sin6_addr, IpAddress, INET6_ADDRSTRLEN);
Port = *(&((sockaddr_in6*)(Result->ai_addr))->sin6_port);
std::cout << "\n IPV6 Address of Domain '" << DomainName << "' Is " << IpAddress << " With Port: " << Port;
IsSuccessful = true;
}
if (!IsSuccessful)// For the safe of readability
{
std::cout << "\n Error GetAddrInfoFromHostName() Failed in NW_P!\n";
}
}
You are bit-shifting the error code 10 bits to the left before assigning it to ErrorCode.
Decimal 11268096 is binary 101010111111000000000000. Notice all those extra zeros on the right?
You need to get rid of << '\n' after getaddrinfo() returns, it doesn't belong there, as you are not outputting the error code to std::cout on that line of code.
Removing the bit shifting, the real error code is 11004 (binary 10101011111100) which is WSANO_DATA:
Valid name, no data record of requested type.The requested name is valid and was found in the database, but it does not have the correct associated data being resolved for. The usual example for this is a host name-to-address translation attempt (using gethostbyname or WSAAsyncGetHostByName) which uses the DNS (Domain Name Server). An MX record is returned but no A record—indicating the host itself exists, but is not directly reachable.
You can pass the error code to gai_strerror() to get a human-readable string for your error message output, eg:
std::cout << "\n Error GetAddrInfoFromHostNameIPV6() Failed with Error Code: " << ErrorCode << " (" << gai_strerror(ErrorCode) << ") in GetAddrInfoFromHostName In: NW_P!";
As for the port number being 0, you are not asking getaddrinfo() to parse any service name/port string as input (the pServiceName parameter is NULL), you are only asking for translating a domain name into an IP, so it is not going to output any port number. Port numbers are not used by Domains themselves. Port numbers are used by services (HTTP, etc) that are running on servers where domains/IPs point to.
On a side note, you are leaking the addrinfo list that getaddrinfo() outputs. You need to call freeaddrinfo() when you are done using the list.
I'm trying to implement a sample hostname validation with OpenSSL.
The sample C/C++ code I have put together is:
// please note I'm connecting to https://openssl.org itself
// enable SNI
if(!SSL_set_tlsext_host_name(ssl, "www.openssl.org")) throw;
if(!SSL_connect(ssl)) throw;
// connection is fine, I can get the homepage via HTTP
X509 *cert = SSL_get_peer_certificate(ssl);
if(cert) {
if(!X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl), "google.com", 0)) throw;
SSL_set_verify(ssl, SSL_VERIFY_PEER, 0);
const long cert_res = SSL_get_verify_result(ssl);
if(cert_res == X509_V_OK) {
printf("Certificate verified!\n");
}
X509_free(cert);
}
As per code above I'm successfully connecting to the openssl.org domain; then I'm setting the name to verify as google.com to test failures, but the code still succeeds.
What am I doing wrong?
How can I implement a thorough verification of hostnames using OpenSSL APIs? I wouldn't want to re-implement (most likely with bugs/wrongly) what is already implemented in the library...
I'm using Ubuntu 16.04 and this libssl version: /lib/x86_64-linux-gnu/libssl.so.1.0.0.
As suggested by jww, one simply needs to set (at least)
if(!X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl), "google.com", 0)) throw;
before performing the connection itself:
if(!SSL_connect(ssl)) throw;
In this case, one case ensure OpenSSL to implement the check automatically at connection time by adding following code:
SSL_set_verify(ssl, SSL_VERIFY_PEER, 0);
before calling SSL_connect, or follow the same path as before and have return X509_V_ERR_HOSTNAME_MISMATCH by SSL_get_verify_result if one wants to handle things in more details:
// please note I'm connecting to https://openssl.org itself
// enable SNI
if(!SSL_set_tlsext_host_name(ssl, "www.openssl.org")) throw;
// enable name/domain verification
if(!X509_VERIFY_PARAM_set1_host(SSL_get0_param(ssl), "google.com", 0)) throw;
if(!SSL_connect(ssl)) throw;
// get certificate
X509 *cert = SSL_get_peer_certificate(ssl);
if(cert) {
const long cert_res = SSL_get_verify_result(ssl);
// in case of name/domain mismatch cert_res will
// be set as 62 --> X509_V_ERR_HOSTNAME_MISMATCH
if(cert_res != X509_V_OK) throw; // certificate has been tampered with
X509_free(cert);
} else throw; // we couldn't get certificate
I am making a Winamp plugin with the single function of sending the details of the song being played over HTTP to a webpage/webserver. I couldn't find anything like this that actually works, so decided to dive in for the first time into C++ and just do it myself.
The plugin is a C++ DLL file. I have got to the point where I can output the song title to a windows message box every time a new song is played (not bad for a C++ first-timer! ;) )
Where am I stuck:
I couldn't find anything to get me in the push notification on C++ direction AT ALL.
I tried, without any success, embedding/including HTTP libraries into my DLL to try post requests instead. libcurl, this library here, and this library too - but couldn't get any of them to work! I just kept getting linking errors and then some. After a few hours I just gave up.
I am a very skilled JavaScript programmer so I thought perhaps using JS to connect into a push notification service can work out but running JS inside C++ looks too complicated to me. Perhaps I'm wrong?
Bottom line, I don't know which solution is better (read: easier) to implement - push notifications or post requests?
I appreciate any solutions/input/directions/information or whatever you got. I can post code but the only code I have is the part getting the song information from Winamp and that part works.
Thanks in advance.
EDIT: I guess it's worth noting I'm using the new VS2012?
That's OK to use these kind of libraries, but when speaking of C++, I mainly think of "pure code", so I like the native style to do something, and do it without the help of libraries.
Thinking of that, I can provide you an example on how to send a HTTP's POST request, using Microsoft's winsock2 library.
First, I'd like to point out that I used this link to have a base on how to use winsock2.h.
Ok, now going on to the code, you need these includes:
#include <string>
#include <sstream>
#include <winsock2.h>
You'll also need to link winsock2 library (or specify it in your project settings, as you use VS2012):
#pragma comment(lib, "ws2_32.lib")
Now, the function I edited from the link (I've just edited it to make it look simpler, and also to do some error check and proper cleanup):
int http_post(char *hostname, char *api, char *parameters, std::string& message)
{
int result;
WSADATA wsaData;
result = WSAStartup(MAKEWORD(1, 1), &wsaData);
if(result != NO_ERROR)
{
//printf("WSAStartup failed: %d\n", result);
return 0;
}
sockaddr_in sin;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == INVALID_SOCKET)
{
//printf("Error at socket(): %ld\n", WSAGetLastError());
WSACleanup();
return 0;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
struct hostent *host_addr = gethostbyname(hostname);
if(host_addr == NULL)
{
//printf("Unable to locate host\n");
closesocket(sock);
WSACleanup();
return 0;
}
sin.sin_addr.s_addr = *((int *)*host_addr->h_addr_list);
if(connect(sock, (const struct sockaddr *)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
//printf("Unable to connect to server: %ld\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 0;
}
std::stringstream stream;
stream << "POST " << api << " HTTP/1.0\r\n"
<< "User-Agent: Mozilla/5.0\r\n"
<< "Host: " << hostname << "\r\n"
<< "Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n"
<< "Content-Length: " << strlen(parameters) << "\r\n"
<< "Accept-Language: en-US;q=0.5\r\n"
<< "Accept-Encoding: gzip, deflate\r\n"
<< "Accept: */*\r\n"
<< "\r\n" << parameters
;
if(send(sock, stream.str().c_str(), stream.str().length(), 0) == SOCKET_ERROR)
{
//printf("send failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 0;
}
if(shutdown(sock, SD_SEND) == SOCKET_ERROR)
{
//printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(sock);
WSACleanup();
return 0;
}
char buffer[1];
do
{
result = recv(sock, buffer, 1, 0);
if(result > 0)
{
//printf("Bytes received: %d\n", result);
message += buffer[0];
}
else if(result == 0)
{
//printf("Connection closed\n");
}
else
{
//printf("recv failed: %d\n", WSAGetLastError());
}
}
while(result > 0);
closesocket(sock);
WSACleanup();
return 1;
}
As you are using a DLL, I commented out the printfs, so you can use your proper output function according to the Winamp plugin API.
Now, to use the function, it's self-explanatory:
std::string post;
http_post("www.htmlcodetutorial.com", "/cgi-bin/mycgi.pl", "user=example123&artist=The Beatles&music=Eleanor Rigby", post);
By doing this, and checking the function return number (either 1 for success or 0 for failure), you can parse the result string returned from the site, and check if everything went OK.
About your last question, using POST request is OK, but you, as a webmaster, know that it's possible to "abuse" this option, such as request flooding, etc. But it really is the simplest way.
If you, by the server side part, parse the data correctly, and check for any improper use of the request, then you can use this method without any problems.
And last, as you are a C++ beginner, you'll need to know how to manipulate std::strings for parsing the song name and artist to the post POST message string safely, if you don't know yet, I recommend this link.
I have written a SOAP client using OpenSSL (written in C++ on Ubuntu 12.04) but it currently works without checking the server security certificate. This is the function I am using to set up the connection and checking the certificate
bool bInitialiseSSL(SSL_CTX* &ctx, SSL* &ssl, BIO* &bio)
{
ctx = SSL_CTX_new(SSLv23_client_method());
bio = BIO_new_ssl_connect(ctx);
if (bio == NULL) {
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return false;
}
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
char target[] = "api.betfair.com:https";
BIO_set_conn_hostname(bio, target);
BIO_set_nbio(bio,1);
while (1) {
if (BIO_do_connect(bio) <= 0) {
if (!BIO_should_retry(bio)) {
cout << "Connect failed." << endl;
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
} else {
break;
}
}
if (BIO_do_handshake(bio) <= 0) {
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
X509 *cert;
bool bValid = false;
cert = SSL_get_peer_certificate(ssl);
if ( cert != NULL ) {
long res = SSL_get_verify_result(ssl);
if (res == X509_V_OK) {
bValid = true;
} else {
cout << "Error in security validation: " << res << endl;
}
X509_free(cert);
}
return bValid;
}
This works fine but the return value of SSL_get_verify_result is 20 which corresponds to
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local
issuer certificate
I have read some of the OpenSSL documentation for their functions but it is not particularly user friendly. I have looked at a number of web tutorials and I cannot see what I am doing wrong. My software worked perfectly before I tried to implement the certificate checking but I cannot see what I need to do. Do I need to configure settings on my machine? The server is betfair which is supposedly very secure and I find it hard to believe that they do not have valid SSL certificates. If anyone can tell me what I am doing wrong I would be very grateful.
It depends on the certificates of the server.
If it is a public valid certificate, you can include the CA certs file into SSL_CTX.
code:
ctx = SSL_CTX_new(SSLv23_client_method());
// You can load CA certs into SSL_CTX
SSL_CTX_load_verify_locations(ctx, cafile, NULL); // cafile: CA PEM certs file
You can download the public CA certs file from cURL website CA Certs from mozilla.org
If it is a private certs, and you have the certificate file, you can use SSL_CTX_use_certificate_file instead of SSL_CTX_load_verify_locations.