Verifying client certificate while adding StartTLS and LDAPS option in LDAP - c++

I am writing an application which will allow user to login to the app using LDAP settings.
Sample program without Start TLS/LDAPS support:
#include <windows.h>
#include <winldap.h>
int main(){
LDAP *ldap =NULL;
int returnCode = -1;
int version = LDAP_VERSION3;
ldap = ldap_init(hostName, PORT);
if (ldap == NULL) {
printf("Failed to init LDAP connection");
return FALSE;
}
returnCode = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, (void*)&version);
if (returnCode != LDAP_SUCCESS) {
cout<<"LDAP: Could not set options. Error: "<< returnCode <<" "<< ldap_err2string(returnCode);
ldap_unbind(ldap);
return FALSE;
}
returnCode = ldap_connect(ldap, NULL);
if (returnCode != LDAP_SUCCESS) {
printf("LDAP: Could not establish connection. Error: %d %s", returnCode, ldap_err2string(returnCode));
ldap_unbind(ldap);
return FALSE;
}
returnCode = ldap_bind_s(ldap, binddn, bindpwd, LDAP_AUTH_SIMPLE);
if (returnCode != LDAP_SUCCESS) {
printf("LDAP: Could not establish connection. Error: %d %s", returnCode, ldap_err2string(returnCode));
ldap_unbind(ldap);
return FALSE;
}
}
StartTLS can be implemented using ldap_start_tls_s function. But I want to verify the certificate before allowing the connections. Any suggestions?

The StarTLS is an extended LDAP operation and must be sent after you have established the connection. However, the server and/or client certificates verifications are part of the TLS protocol. So when you use ldap_start_tls_s function, it will be performed automatically.
If you want to verify the server certificate at the time of the connection, you should use LDAP over SSL, and connect to the LDAPS port. You can use the ldap_sslinit() method for that.

Related

SOCKET connection problems in a service on Windows Server 2012

I inherited a C++/Windows project where we have an SNMP extension agent (loaded by SNMP service). Inside the agent, we are creating a simple TCP server to which our client applications connect and provide it with data for SNMP queries/traps etc. This all seems to work fine on Windows Server 2008. However, on Windows Server 2012, the client can no longer connect to the server running inside the agent (in SNMP service). The connect() fails with error 10013.
My server code looks something like this:
fd_set master_set;
fd_set readfds;
SOCKET listener;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
{
OutputDebugStringA("WSAStartup failed\n");
return -1;
}
FD_ZERO(&master_set);
FD_ZERO(&readfds);
//----------------------
// Create a SOCKET for listening for
// incoming connection requests.
listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listener == INVALID_SOCKET) {
OutputDebugStringA("socket failed with error:\n");
return -1;
}
int reuse_addr = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse_addr, sizeof(reuse_addr));
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port for the socket that is being bound.
sockaddr_in service = { 0 };
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27015);
if (bind(listener, (SOCKADDR *)& service, sizeof(service)) == SOCKET_ERROR)
{
printf("bind failed with error: %d \n", WSAGetLastError());
closesocket(listener);
return -1;
}
if (listen(listener, 5) == SOCKET_ERROR)
{
OutputDebugStringA("listen failed with error\n");
closesocket(listener);
return -1;
}
u_long NonBlock = 1;
if (ioctlsocket(listener, FIONBIO, &NonBlock) == SOCKET_ERROR)
{
OutputDebugStringA("ioctlsocket() failed with error\n");
return -1;
}
FD_SET(listener, &master_set);
timeval timeout;
timeout.tv_sec = 3;
timeout.tv_usec = 0;
printf("Started Server on port %d\n", 27015);
for (;;)
{
readfds = master_set;
int ret = select(0, &readfds, NULL, NULL, &timeout);
if (ret == 0)
{
// Time out // Check if we need to shutdown
continue;
}
if (ret < 0)
{
printf("Error in Socket select\n");
return -1;
}
for (int i = 0; i < readfds.fd_count; i++)
{
SOCKET xfd = readfds.fd_array[i];
if (xfd == listener)
{
// New Connection.
SOCKET new_fd = HandleNewConnection(listener);
if (new_fd == -1)
{
printf("Error Accepting new connection");
continue;
}
FD_SET(new_fd, &master_set);
printf("Accepted new Connection\n");
continue;
}
else
{
if (!HandleIncomingData(xfd))
{
closesocket(xfd);
FD_CLR(xfd, &master_set);
continue;
}
}
}
}
SOCKET HandleNewConnection(SOCKET listener)
{
SOCKET newfd = accept(listener, (sockaddr*)NULL, (int*)NULL);
u_long NonBlock = 1;
ioctlsocket(newfd, FIONBIO, &NonBlock);
return newfd;
}
bool HandleIncomingData(SOCKET fd)
{
char buffer[16] = { 0 };
int recv_bytes = -1;
if ((recv_bytes = recv(fd, buffer, 16, 0)) <= 0)
{
printf("Connection Closed/ Error in Recieving");
return false;
}
printf("recieved %d bytes\n", recv_bytes);
return true;
}
The select continues to timeout every 3 seconds without any connection getting accepted.
Here's all that I have tried (none worked):
Tried to run the service in a specific user account.
The server is run in a separate thread, I provided a SECURITY_ATTRIBUTE with NULL DACL to see if it's a security problem.
Tried different ports.
Tried same server code in a separate normal application. The client can connect to this application.
Sample server application when launched from the agent, the client cannot connect to it.
Windows firewall is turned off and I don't have any anti virus software installed which would block such connections.
Checked connection from outside and observed in Wireshark that the TCP SYN packet does arrive but there's no response to it.
Observed in Process Explorer TCP/IP properties that the SNMP service does have a TCP socket listening on 127.0.0.1:27015.
For quick tests I am just doing telnet to port 27015.
Is there something obviously wrong with the server code which I am missing?
Is there some security restriction in Windows Server 2012 which don't allow a service to accept such TCP connections?
Any other hints, comments, inputs?
I solved the problem. The issue was due to Windows Service Hardening which did not allow any TCP communication from snmp service (and extensions). This is enforced even if the firewall is turned off.
https://support.microsoft.com/en-us/kb/2771908
I could solve it following these steps (found in http://www-01.ibm.com/support/docview.wss?uid=nas7ba16117761f1f93b86257f73000cff77)
Log on the system as Administrator and open Registry by issuing regedit in the command prompt.
Navigate to [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\RestrictedServices\Static\System].
Find the values which meet the following points:
a. “Name” string starts with “SNMP-”.
b. “Data” string contains “syswow64\snmp.exe”.
c. “Data” string contains “Action=Block”.
Change the “Action=Block” to “Action=Allow” of those entries.
Restart the “Windows Firewall” service by issuing net stop MPSSVC and net start MPSSVC .
Restart the “SNMP Service” service by using net stop SNMP and net start SNMP .

winldap, Qt, ldap_search_ext_s returns "Operations Error" after ldap_sasl_bind_s

I am trying to search on an openldap server using the winldap library in Qt on windows.
The response I get is (replaced the values with "some*"):
"LDAP using normal connection to ldap://someip:someport"
[10:37:30][Debug]"Connected to LDAP successfully"
[10:37:30][Debug]"base:ou=someou,dc=somedc,dc=someotherdc, scope:1, filter:(objectClass=*)"
[10:37:30][Warning]"Failed to search entries in LDAP server: Operations Error"
The server has the following error:
Jan 21 05:38:35 someservername slapd[2348]: connection_operation: error: SASL bind in progress (tag=99).
Jan 21 05:38:35 someservername slapd[2348]: connection_operation: error: SASL bind in progress (tag=66).
The code responible for the connection is:
int LdapInstance::connectToServer()
{
int ret = -1;
int ldapVersion = LDAP_VERSION3;
if(!access.isValid())
{
qWarning() << QString("Failed to bind LDAP server. Access information invalid");
lastErrorString = QString("Access information invalid");
return -4;
}
QString uri;
if(access.useSsl())
{
uri = QString("ldaps://%1:%2").arg(access.getServer()).arg(access.getPort());
qDebug() << QString("LDAP using TLS connection to %1").arg(uri);
}
else
{
uri = QString("ldap://%1:%2").arg(access.getServer()).arg(access.getPort());
qDebug() << QString("LDAP using normal connection to %1").arg(uri);
}
ld = ldap_init((const PWCHAR)access.getServer().utf16(), access.getPort());
if(ld == NULL)
{
qWarning() << QString("Failed to init LDAP instance: %1").arg(QString::fromWCharArray(ldap_err2string(ret)));
lastErrorString = QString::fromWCharArray(ldap_err2string(ret));
return -1;
}
if((ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion)) != LDAP_SUCCESS)
{
qWarning() << QString("Failed to set LDAP option: %1").arg(QString::fromWCharArray(ldap_err2string(ret)));
lastErrorString = QString::fromWCharArray(ldap_err2string(ret));
return -2;
}
if((ret = ldap_connect(ld, NULL)) != LDAP_SUCCESS)
{
qWarning() << QString("Failed to connect to LDAP instance: %1").arg(QString::fromWCharArray(ldap_err2string(ret)));
lastErrorString = QString::fromWCharArray(ldap_err2string(ret));
return -1;
}
struct berval cred;
cred.bv_len = access.getPasswd().length();
cred.bv_val = (const PCHAR)access.getPasswd().utf16();
if(( ret = ldap_sasl_bind_s(ld,
(const PWCHAR)access.getLoginDn().utf16(),
L"DIGEST-MD5",
&cred,
NULL,
NULL,
NULL)) != LDAP_SUCCESS)
{
qWarning() << QString("Failed to bind LDAP server %1: %2").arg(access.getLoginDn()).arg(QString::fromWCharArray(ldap_err2string(ret)));
lastErrorString = QString::fromWCharArray(ldap_err2string(ret));
return -3;
}
qDebug() << QString("Connected to LDAP successfully");
return 0;
}
The search funktion is:
QList<SimpleFriend *> LdapInstance::getSimpleFriendsByGroup(const QString &group, int *response)
{
Q_ASSERT(!group.isEmpty() && response);
LDAPMessage *message = NULL;
PWCHAR attrList[] = {
L"displayName",
L"givenName",
L"sn",
L"o",
L"telephoneNumber",
L"homePhone",
L"mobile",
NULL
};
QList<SimpleFriend *> simpleFrList;
int ret = connectToServer();
if(ret != 0)
{
qWarning() << QString("Failed to get simple friend list from LDAP server. Connection failed.");
*response = ret;
return simpleFrList;
}
QString base = QString("%1,%2").arg(group).arg(access.getBaseDc());
qDebug() << QString("base:%1, scope:%2, filter:%3")
.arg(base)
.arg(LDAP_SCOPE_ONELEVEL)
.arg(access.getSearchFilter());
// HERE BE DRAGONS =(
if((ret = ldap_search_ext_s(ld,
(const PWCHAR)base.utf16(),
LDAP_SCOPE_ONELEVEL,
(const PWCHAR)access.getSearchFilter().utf16(),
NULL,//attrList,
0,
NULL,
NULL,
NULL,
0,
&message)) != LDAP_SUCCESS)
{
qWarning() << QString("Failed to search entries in LDAP server: %1").arg(QString::fromWCharArray(ldap_err2string(ret)));
lastErrorString = QString::fromWCharArray(ldap_err2string(ret));
*response = ret;
closeConnection();
return simpleFrList;
}
//... some processing
ldap_msgfree(message);
closeConnection();
*response = 0;
return simpleFrList;
}
I do not know what I am doing wrong. I am converting everything to PCHAR and PWCHAR before passing it to winldap functions.
I ported this to windows from my Linux implementation using the openldap library (There everything workes fine... naturally =))
Could it be that server is requireing some additional information to fully bind? ... but what?
Please help me... Would save my day.
The errormessage from winldap is not verry good.
Turns out you will need to aktivate sasl on the LDAP server =).
In addition winldap needs you to implement the sasl handshake yourself.

LDAP's ldap_search_s() fails on Windows Active Directory

I have setup an Active Directory service on my Windows 2008 server.
I have added an user and here is the DN (DistingushedName) CN=ashwin,CN=Users,DC=test,DC=com
There is no password set for the DN and anonymous binds are allowed. I have a sample (test code) C++ program that connects to AD and searches the user.
#include "windows.h"
#include "winldap.h"
#include "stdio.h"
// Entry point for your application
int main(int argc, char* argv[])
{
LDAP* pLdapConnection = NULL;
INT returnCode = 0;
INT connectSuccess = 0;
ULONG version = LDAP_VERSION3;
LONG lv = 0;
int option(0);
LDAPMessage *vLdapMessage;
// Initialize an LDAP session without SSL.
pLdapConnection = ldap_init("192.168.56.128",389);
if (pLdapConnection == NULL)
{
printf( "ldap_init failed with 0x%x.\n",hr);
return -1;
}
// Specify version 3; the default is version 2.
returnCode = ldap_set_option(pLdapConnection,
LDAP_OPT_PROTOCOL_VERSION,
(void*)&version);
if (returnCode != LDAP_SUCCESS)
goto FatalExit;
//Turning off referrals
ldap_set_option(pLdapConnection, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); // required
// Connect to the server.
connectSuccess = ldap_connect(pLdapConnection, NULL);
if(connectSuccess != LDAP_SUCCESS)
{
printf("ldap_connect failed with 0x%x.\n",connectSuccess);
goto FatalExit;
}
// Bind with current credentials.
printf("Binding ...\n");
returnCode = ldap_bind_s(pLdapConnection,NULL, NULL, LDAP_AUTH_SIMPLE);
if (returnCode != LDAP_SUCCESS)
goto FatalExit;
returnCode = ldap_search_s(pLdapConnection, "DC=test, DC=com", LDAP_SCOPE_SUBTREE, "CN=ashwin", NULL, 0, &vLdapMessage);
if (returnCode != LDAP_SUCCESS)
goto FatalExit;
NormalExit:
if (pLdapConnection != NULL)
ldap_unbind_s(pLdapConnection);
return 0;
FatalExit:
if( pLdapConnection != NULL )
ldap_unbind_s(pLdapConnection);
printf( "\n\nERROR: 0x%x\n", returnCode);
return returnCode;
}
The search fails. ldap_search_s always returns 1.
The same setup testing on Apache directory service works fine.
Could someone point why this does not work with Windows AD? what is wrong in the program?
Active Directory filtering syntax can be quite verbose. From what I can tell, you just need to modify your filter slightly. Try this :
(&(objectClass=user)(distinguishedName=CN=ashwin,CN=Users,DC=test,DC=com))
However, for single user filtering, I'd try using the sAMAccountName. This generally follows a {FirstInitial}{LastName} format, and would be unique to the user (Ex. JSmith) :
(&(objectClass=user)(sAMAccountName=JSmith))

Source for a Cpp OpenSSL server with errors

I have a problem with some source code regarding OpenSSL and Cpp. For some reason it runs fine but doesn't open a socket! When I try to connect to it using s_client I get 'Connect: No Error". When I run netstat I get no open port. It should open a port on 12120. I even disabled my firewall temporarly and it didn't help! BTW I'm using Windows 7. Thanks for your consideration! My program just says everythings fine and it starts blocking at the second *BIO_do_accept( abio );*
#include "stdio.h"
#include "string.h"
#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
int password_callback(char *buf, int size, int rwflag, void *userdata)
{
/* For the purposes of this demonstration, the password is "dw" */
printf("*** Callback function called\n");
strcpy(buf, "dw");
return 1;
}
int main()
{
SSL_CTX *ctx;
SSL *ssl;
BIO *bio, *abio, *out, *sbio;
int (*callback)(char *, int, int, void *) = &password_callback;
printf("Secure Programming with the OpenSSL API, Part 4:\n");
printf("Serving it up in a secure manner\n\n");
SSL_load_error_strings();
ERR_load_BIO_strings();
SSL_library_init();
ERR_load_SSL_strings();
OpenSSL_add_all_algorithms();
printf("Attempting to create SSL context... ");
ctx = SSL_CTX_new( SSLv23_server_method() );
if(ctx == NULL)
{
printf("Failed. Aborting.\n");
return 0;
}
printf("\nLoading certificates...\n");
SSL_CTX_set_default_passwd_cb(ctx, callback);
if(!SSL_CTX_use_certificate_file(ctx, "certificate.pem", SSL_FILETYPE_PEM))
{
ERR_print_errors_fp(stdout);
SSL_CTX_free(ctx);
return 0;
}
if(!SSL_CTX_use_PrivateKey_file(ctx, "private.key", SSL_FILETYPE_PEM))
{
ERR_print_errors_fp(stdout);
SSL_CTX_free(ctx);
return 0;
}
printf("Attempting to create BIO object... ");
bio = BIO_new_ssl(ctx, 0);
if(bio == NULL)
{
printf("Failed. Aborting.\n");
ERR_print_errors_fp(stdout);
SSL_CTX_free(ctx);
return 0;
}
printf("\nAttempting to set up BIO for SSL...\n");
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
abio = BIO_new_accept("12120");
BIO_set_accept_bios(abio, bio);
printf("Waiting for incoming connection...\n");
if(BIO_do_accept(abio) <= 0)
{
ERR_print_errors_fp(stdout);
SSL_CTX_free(ctx); SSL_library_init();
BIO_free_all(bio);
BIO_free_all(abio);
return 0;
}
if(BIO_do_accept(abio) <= 0)
{
ERR_print_errors_fp(stdout);
SSL_CTX_free(ctx);
BIO_free_all(bio);
BIO_free_all(abio);
return 0;
}
out = BIO_pop(abio);
if(BIO_do_handshake(out) <= 0)
{
printf("Handshake failed.\n");
ERR_print_errors_fp(stdout);
SSL_CTX_free(ctx);
BIO_free_all(bio);
BIO_free_all(abio);
return 0;
}
BIO_puts(out, "Hello\n");
BIO_flush(out);
BIO_free_all(out);
BIO_free_all(bio);
BIO_free_all(abio);
SSL_CTX_free(ctx);
}
What exactly are you expecting here? You should see port 12120 in LISTENING state. Your client should then be able to connect. Your server then does another accept, which will prevent it reading any I/O on the accepted port, and will ultimately block your client too. I don't know why you are doing two accept's in a row but it's your code.
From the OpenSSL Documentation for BIO_do_accept():
BIO_do_accept() serves two functions. When it is first called, after
the accept BIO has been setup, it will attempt to create the accept
socket and bind an address to it. Second and subsequent calls to
BIO_do_accept() will await an incoming connection, or request a retry
in non-blocking mode.

More Cpp, Winsock, RSA and OpenSSL

First off I want to warn you there is alot of code here! I want the client to connect to the server using RSA with the OpenSSL library. When I launch the server everything is fine but when I try and connect with the client the connection failed with Error: error 1408A0C1: SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher. Thanks for your consideration but I have to warn you again there is alot of code here. If you are interested I am creating a TCP connection to a Java applet protected with RSA. This is not my program I found it all jumbled on the 'net. If you could add your own simple source for this purpose in the comments I would appreciate it!
// THIS IS THE CLIENT FOR THE CONNECTION
#include <openssl/bio.h> // BIO objects for I/O
#include <openssl/ssl.h> // SSL and SSL_CTX for SSL connections
#include <openssl/err.h> // Error reporting
#include <stdio.h> // If you don't know what this is for stop reading now.
void openssltest(void);
int main(int argc, char** argv) {
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
ERR_load_BIO_strings(); // Load BIO error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
openssltest(); // We'll define this later.
return 0;
}
void openssltest() {
// Set up a SSL_CTX object, which will tell our BIO object how to do its work
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
// Create a SSL object pointer, which our BIO object will provide.
SSL* ssl;
// Create our BIO object for SSL connections.
BIO* bio = BIO_new_ssl_connect(ctx);
// Failure?
if (bio == NULL) {
printf("Error creating BIO!\n");
ERR_print_errors_fp(stderr);
// We need to free up the SSL_CTX before we leave.
SSL_CTX_free(ctx);
return;
}
// Makes ssl point to bio's SSL object.
BIO_get_ssl(bio, &ssl);
// Set the SSL to automatically retry on failure.
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
// We're connection to google.com on port 443.
BIO_set_conn_hostname(bio, "127.0.0.1:6789");
// Same as before, try to connect.
if (BIO_do_connect(bio) <= 0) {
printf("Failed to connect!");
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
// Now we need to do the SSL handshake, so we can communicate.
if (BIO_do_handshake(bio) <= 0) {
printf("Failed to do SSL handshake!");
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
// Create a buffer for grabbing information from the page.
char buf[1024];
memset(buf, 0, sizeof(buf));
// Create a buffer for the reqest we'll send to the server
char send[1024];
memset(send, 0, sizeof(send));
// Create our GET request.
strcat(send, "GET / HTTP/1.1\nHost:google.com\nUser Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)\nConnection: Close\n\n");
// BIO_puts sends a null-terminated string to the server. In this case it's our GET request.
BIO_puts(bio, send);
// Loop while there's information to be read.
while (1) {
// BIO_read() reads data from the server into a buffer. It returns the number of characters read in.
int x = BIO_read(bio, buf, sizeof(buf) - 1);
// If we haven't read in anything, assume there's nothing more to be sent since we used Connection: Close.
if (x == 0) {
break;
}
// If BIO_read() returns a negative number, there was an error
else if (x < 0) {
// BIO_should_retry lets us know if we should keep trying to read data or not.
if (!BIO_should_retry(bio)) {
printf("\nRead Failed!\n");
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
}
// We actually got some data, without errors!
else {
// Null-terminate our buffer, just in case
buf[x] = 0;
// Echo what the server sent to the screen
printf("%s", buf);
}
}
// Free up that BIO object we created.
BIO_free_all(bio);
// Remember, we also need to free up that SSL_CTX object!
SSL_CTX_free(ctx);
// Return.
return;
}
// THIS IS THE SERVER FOR THE CONNECTION
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <iostream>
#include <stdio.h>
#include <winsock2.h>
#define PASSWORD "passme"
void serverThread();
int main(int argc, char** argv) {
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
SSL_library_init(); // Initialize OpenSSL's SSL libraries
SSL_load_error_strings(); // Load SSL error strings
ERR_load_BIO_strings(); // Load BIO error strings
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
serverThread();
return 0;
}
void serverThread() {
// First, we need to initialize Winsock.
WSADATA wsadata;
int ret = WSAStartup(0x101, &wsadata);
if (ret != 0) {
printf("WSAStartup() failed with: %d!\n", GetLastError());
return;
}
// Next we need to create a server socket.
SOCKET server = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in sockaddrin;
// Internet socket
sockaddrin.sin_family = AF_INET;
// Accept any IP
sockaddrin.sin_addr.s_addr = INADDR_ANY;
// Use port 6789
sockaddrin.sin_port = htons(6789);
// Valid socket?
if (server == INVALID_SOCKET) {
printf("Error creating server socket!");
return;
}
// Now bind to the port
ret = bind(server, (sockaddr*) &(sockaddrin), sizeof(sockaddrin));
if (ret != 0) {
printf("Error binding to port!\n");
return;
}
// Start listening for connections
// Second param is max number of connections
ret = listen(server, 50);
if (ret != 0) {
printf("Error listening for connections!\n");
return;
}
// Set up to accept connections
SOCKET client;
sockaddr_in clientsockaddrin;
int len = sizeof(clientsockaddrin);
printf("Server ready to accept connections!\n");
while (1) {
// Block until a connection is ready
client = accept(server, (sockaddr*) &clientsockaddrin, &len);
printf("Connection recieved from %s!\n", inet_ntoa(clientsockaddrin.sin_addr));
// Notice that we use server_method instead of client_method
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
BIO* bio = BIO_new_file("dh1024.pem", "r");
// Did we get a handle to the file?
if (bio == NULL) {
printf("Couldn't open DH param file!\n");
break;
}
// Read in the DH params.
DH* ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
// Free up the BIO object.
BIO_free(bio);
// Set up our SSL_CTX to use the DH parameters.
if (SSL_CTX_set_tmp_dh(ctx, ret) < 0) {
printf("Couldn't set DH parameters!\n");
break;
}
// Now we need to generate a RSA key for use.
// 1024-bit key. If you want to use something stronger, go ahead but it must be a power of 2. Upper limit should be 4096.
RSA* rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
// Set up our SSL_CTX to use the generated RSA key.
if (!SSL_CTX_set_tmp_rsa(ctx, rsa)) {
printf("Couldn't set RSA key!\n");
// We don't break out here because it's not a requirement for the RSA key to be set. It does help to have it.
}
// Free up the RSA structure.
RSA_free(rsa);
SSL_CTX_set_cipher_list(ctx, "ALL");
// Set up our SSL object as before
SSL* ssl = SSL_new(ctx);
// Set up our BIO object to use the client socket
BIO* sslclient = BIO_new_socket(client, BIO_NOCLOSE);
// Set up our SSL object to use the BIO.
SSL_set_bio(ssl, sslclient, sslclient);
// Do SSL handshaking.
int r = SSL_accept(ssl);
// Something failed. Print out all the error information, since all of it may be relevant to the problem.
if (r != 1) {
printf("SSL_accept() returned %d\n", r);
printf("Error in SSL_accept(): %d\n", SSL_get_error(ssl, r));
char error[65535];
ERR_error_string_n(ERR_get_error(), error, 65535);
printf("Error: %s\n\n", error);
ERR_print_errors(sslclient);
int err = WSAGetLastError();
printf("WSA: %d\n", err);
break;
}
}
}
int password_callback(char* buffer, int num, int rwflag, void* userdata) {
if (num < (strlen(PASSWORD) + 1)) {
return(0);
}
strcpy(buffer, PASSWORD);
return strlen(PASSWORD);
}
int verify_callback(int ok, X509_STORE_CTX* store) {
char data[255];
if (!ok) {
X509* cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
printf("Error with certificate at depth: %d!\n", depth);
X509_NAME_oneline(X509_get_issuer_name(cert), data, 255);
printf("\tIssuer: %s\n", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, 255);
printf("\tSubject: %s\n", data);
printf("\tError %d: %s\n", err, X509_verify_cert_error_string(err));
}
return ok;
}
Usually that error indicates you have not configured an RSA key in your server. In order to use RSA ciphersuites your server must have a certificate with an RSA key properly configured.