OpenLDAP - Enabling CRL check for LDAP TLS connections - ssl

I have a client that connects to LDAP server using TLS. For this connection, I want to enable CRL check and reject the connection only if any server/client certificates are revoked.
In special cases (like CRL missing, CRL expired) I want to ignore the error and establish the connection.
So I though to overwrite the default SSL verify call back to ignore the specific errors.
But the call back is not called at all. Always only default call-back is called.
Here is my call back:
static int verify_callback(int ok, X509_STORE_CTX *ctx)
{
X509* cert = X509_STORE_CTX_get_current_cert(ctx);
if (ok)
return ok;
int sslRet = X509_STORE_CTX_get_error(ctx);
const char* err = NULL;
switch (sslRet)
{
case X509_V_ERR_UNABLE_TO_GET_CRL:
case X509_V_ERR_CRL_HAS_EXPIRED:
case X509_V_ERR_CRL_NOT_YET_VALID:
printf( "CRL: Verification failed... but ignored : %d\n", sslRet);
return 1;
default:
err = X509_verify_cert_error_string(sslRet);
if (err)
printf( "CRL: Failed to verify : %s\n",err);
return 0;
}
return sslRet;
}
Default verify call-back is overwritten using the ldap call-back set option:
void ldap_tls_cb(LDAP * ld, SSL * ssl, SSL_CTX * ctx, void * arg)
{
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER , verify_callback);
printf("verify call back is set...\n");
return;
}
Main Program:
int main( int argc, char **argv )
{
LDAP *ldap;
int auth_method = LDAP_AUTH_SIMPLE; //LDAP_AUTH_SASL
int ldap_version = LDAP_VERSION3;
char *ldap_host = "10.104.40.35";
int ldap_port = 389;
if ( (ldap = ldap_init(ldap_host, ldap_port)) == NULL ) {
perror( "ldap_init failed" );
return( EXIT_FAILURE );
}
int result = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option failed!");
return(EXIT_FAILURE);
}
int requireCert = LDAP_OPT_X_TLS_DEMAND;
result = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &requireCert);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option - req cert -failed!");
return(EXIT_FAILURE);
}
result = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, "/etc/certs/Cert.pem");
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option - cert file - failed!");
return(EXIT_FAILURE);
}
int crlvalue = LDAP_OPT_X_TLS_CRL_ALL;
result =ldap_set_option(NULL, LDAP_OPT_X_TLS_CRLCHECK, &crlvalue);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "ldap_set_option failed!");
return(EXIT_FAILURE);
}
int debug = 7;
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
result = ldap_set_option(ldap, LDAP_OPT_X_TLS_CONNECT_CB, (void *)ldap_tls_cb);
if (result != LDAP_SUCCESS) {
fprintf(stderr, "ldap_set_option(LDAP_OPT_X_TLS_CONNECT_CB): %s\n", ldap_err2string(result));
return(1);
}
int msgidp = 0;
result = ldap_start_tls(ldap,NULL,NULL,&msgidp);
if (result != LDAP_OPT_SUCCESS ) {
ldap_perror(ldap, "start tls failed!");
return result;
} else {
printf("Start tls success.\n");
}
LDAPMessage *resultm;
struct timeval timeout;
result = ldap_result(ldap, msgidp, 0, &timeout, &resultm );
if ( result == -1 || result == 0 ) {
printf("ldap_result failed;retC=%d \n", result);
return result;
}
result = ldap_parse_extended_result(ldap, resultm, NULL, NULL, 0 );
if ( result == LDAP_SUCCESS ) {
result = ldap_install_tls (ldap);
printf("installing tls... %s\n", ldap_err2string(result));
}
int request_id = 0;
result = ldap_sasl_bind(ldap, "", LDAP_SASL_SIMPLE, NULL, 0, 0, &request_id);
if ( result != LDAP_SUCCESS ) {
fprintf(stderr, "ldap_x_bind_s: %s\n", ldap_err2string(result));
printf("LDAP bind error .. %d\n", result);
return(EXIT_FAILURE);
} else {
printf("LDAP connection successful.\n");
}
ldap_unbind(ldap);
return(EXIT_SUCCESS);
}
can someone help to check why my verify call-back is not called?

I think you need to set the callback on the SSL object directly instead of the context, so
void ldap_tls_cb(LDAP * ld, SSL * ssl, SSL_CTX * ctx, void * arg)
{
SSL_set_verify(ssl, SSL_VERIFY_PEER, verify_callback);
printf("verify call back is set...\n");
return;
}
The reason for this is that the SSL handle has already been initialised by the time your connect callback is called (see the OpenLDAP code), and
it's too late to set this callback through the context at that point:
If no special callback was set before, the default callback for the underlying ctx is used, that was valid at the time ssl was created with SSL_new(3).
OpenLDAP can be built with GnuTLS, so you may need to check that it's using OpenSSL before setting the callback. The LDAP_OPT_X_TLS_PACKAGE option could be used for this (note that I haven't tested this code):
char* package = NULL;
int result = ldap_get_option(NULL, LDAP_OPT_X_TLS_PACKAGE, (void *)&package);
if (result != LDAP_OPT_SUCCESS) {
ldap_perror(ldap, "ldap_get_option failed!");
return(EXIT_FAILURE);
} else {
if (strcmp(package, "OpenSSL") == 0) {
// Set your callback
}
ldap_memfree(package);
}

Related

OpenSSL 1.0.1 SSL_read() function return 0 byte on certain https Websites

I'm trying to make a https client by openssl 1.0.1u that can visit websites with ssl protocol.
When visiting most of https websites (like google.com, yahoo.com, facebook.com, ...), it works well and the home page content is returned. However, there are certain websites (relatively small websites), the server returns me 0 bytes, here are some details:
I use SSLv23_method() to create my openssl context:
this->_sslContext = SSL_CTX_new(SSLv23_method()); // SSLv23_method: Negotiate highest available SSL/TLS version
Then I found that in the following calling sequence (listed forwardly):
(ssl_lib.c) SSL_read(SSL *s, void *buf, int num) ---->
(s3_lib.c) ssl3_read(SSL *s, void *buf, int len) ---->
(s3_lib.c) ssl3_read_internal(SSL *s, void *buf, int len, int peek) ---->
(s3_pkt.c) int ssl3_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek)
With some website (failed case), the function SSL_read() return 0 bytes because inside the function ssl3_read_bytes(), I got a alert_descr set to SSL_AD_CLOSE_NOTIFY then the function simply return 0, here is the source code:
...
if (alert_level == SSL3_AL_WARNING)
{
s->s3->warn_alert = alert_descr;
if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
s->shutdown |= SSL_RECEIVED_SHUTDOWN;
return (0);
}
Anyone can give me any hint to fix this problem? Thanks.
=== UPDATE ===
Upon Steffen Ullrich's suggestion, I post source code that sends request / gets respone. My small experimental https client is composed of Socket and SSLSocket classes and a helper WebpageFetcher class. The function WebpageFetcher::fetchPage is used to send the https request and get the respond from private function WebpageFetcher::_getResponse():
wchar_t * WebpageFetcher::fetchPage(wchar_t * url, int port, bool useSSL)
{
wchar_t * response = NULL;
Socket * socket = Socket::createSocket(false, useSSL);
if (socket == nullptr)
{
response = String(L"Connection failed. Unable to create a SSLSocket!\n").toCharArray();
return response;
}
if (!socket->connect(url, port))//Connection failed
{
response = String(L"Connection failed. Possible reason: Wrong server URL or port.\n").toCharArray();
}
else //Connection succeeded
{
//Send request to server socket
static const char * REQUEST = "GET / \r\n\r\n";
static const int REQUEST_LEN = (const int)strlen(REQUEST);
socket->send((void *)REQUEST, REQUEST_LEN);
//Get the response from server
response = _getResponse(socket);
socket->shutDown();
socket->close();
}
delete socket;
return response;
}
// ============================================================================
wchar_t * WebpageFetcher::_getResponse(Socket * socket)
{
static const int READSIZE = 1024; //Reading buffer size, the larger the better performance
int responseBufferSize = READSIZE + 1;
char * readBuf = new char[READSIZE];
char * responseBuf = new char[responseBufferSize];
int bytesReceived;
int totalBytesReceived = 0;
while ((bytesReceived = socket->recv(readBuf, READSIZE)) > 0)
{
// Check if need to expand responseBuf size
if (totalBytesReceived + bytesReceived >= responseBufferSize)//No enough capacity, expand the response buffer
{
responseBufferSize += READSIZE;
char * tempBuf = new char[responseBufferSize];
memcpy(tempBuf, responseBuf, totalBytesReceived);
delete[] responseBuf;
responseBuf = tempBuf; //Response buffer expanded
}
// Append data from readBuf
memcpy(responseBuf + totalBytesReceived, readBuf, bytesReceived);
totalBytesReceived += bytesReceived;
responseBuf[totalBytesReceived] = '\0';
}
wchar_t * response = (wchar_t *)(totalBytesReceived == 0 ? //Generate the response as a C wide string
String(L"Received nothing from server. Possible reason: Wrong port.\n").toCharArray() :
StringUtil::charsToWchars(responseBuf));
delete[] readBuf;
delete[] responseBuf;
return response;
}
I passed argument useSSL with true when call factory function Socket::createSocket() so that the socket I got is a SSLSocket instance, which overrides the default functions connect(), _send() and _recv() to let openssl to do the actual job. Here is the constructor of my SSLSocket class, which derives from class Socket:
SSLSocket::SSLSocket(bool isServerSocket, int port, int socketType, int socketProtocol, int uOptions, wchar_t * strBindingAddress, wchar_t * cerPath, wchar_t * keyPath, wchar_t * keyPass) :
Socket(isServerSocket, port, socketType, socketProtocol, uOptions, strBindingAddress)
{
// Register the error strings
SSL_load_error_strings();
// Register the available ciphers and digests
SSL_library_init();
// Create an SSL_CTX structure by choosing a SSL/TLS protocol version
this->_sslContext = SSL_CTX_new(SSLv23_method()); // Use SSL 2 or SSL 3
// Create an SSL struct (client only, server does not need one)
this->_sslHandle = (this->_isServer ? NULL : SSL_new(this->_sslContext));
bool success = false;
if (!this->_isServer) // is Client socket
{
success = (this->_sslHandle != NULL);
}
else if (cerPath != NULL && keyPath != NULL) // is Server socket
{
success = ......
}
if (!success)
this->close();
}
And the followings are the functions override the virtual functions in parent class Socket, which lets openssl to do the relevant job:
bool SSLSocket::connect(wchar_t * strDestination, int port, int timeout)
{
SocketAddress socketAddress(strDestination, port);
return this->connect(&socketAddress, timeout);
}
bool SSLSocket::connect(SocketAddress * sockAddress, int timeout)
{
bool success =
(this->_sslHandle != NULL &&
Socket::connect(sockAddress, timeout) && // Regular TCP connection
SSL_set_fd(this->_sslHandle, (int)this->_hSocket) == 1 && // Connect the SSL struct to our connection
SSL_connect(this->_sslHandle) == 1); // Initiate SSL handshake
if (!success)
this->close();
return success;
}
int SSLSocket::_recv(void * lpBuffer, int size, int flags)
{
MonitorLock cs(&_mutex);
return SSL_read(this->_sslHandle, lpBuffer, size);
}
int SSLSocket::_send(const void * lpBuffer, int size, int flags)
{
return SSL_write(this->_sslHandle, lpBuffer, size);
}

TLS Session Resumption with OpenSSL server and SChannel client

I must use the RFC5077 TLS session resumption. My Client use Windows SChannel and server usually uses OpenSSL. In my test, following result.
OpenSSL 1.1.0 (or later) and SChannel: Always session reused, SChannel send previous Session Ticket.
OpenSSL 1.0.2 (any revision) and Schannel: Always new session, SChannel does not send Session Ticket.
OpenSSL and OpenSSL: Always session reused.
So I want to know that
Why Schannel don't use TLS session resumption only for OpenSSL 1.0.2?
The difference between 1.0.2 and 1.1.0.
How to use TLS session resumption at OpenSSL 1.0.2 and SChannel?
Server code: Simple TLS Server
Client code: Windows C++
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define SECURITY_WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>
#include <sspi.h>
#include <schannel.h>
#include <stdio.h>
#include <vector>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Secur32.lib")
struct WSA {
WSA() {
WSADATA wsaData;
if (auto result = WSAStartup(WINSOCK_VERSION, &wsaData))
throw result;
}
~WSA() {
WSACleanup();
}
};
struct Credential : CredHandle {
Credential() {
SCHANNEL_CRED cred = { .dwVersion = SCHANNEL_CRED_VERSION, .dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION };
if (auto ss = AcquireCredentialsHandleW(nullptr, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, nullptr, &cred, nullptr, nullptr, this, nullptr); ss != SEC_E_OK)
throw ss;
}
~Credential() {
FreeCredentialsHandle(this);
}
};
struct Socket {
SOCKET s;
Socket(const char* target, int port) {
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(target);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET)
throw WSAGetLastError();
if (connect(s, reinterpret_cast<const SOCKADDR*>(&addr), sizeof addr))
throw WSAGetLastError();
u_long val = 1;
ioctlsocket(s, FIONBIO, &val);
}
~Socket() {
closesocket(s);
}
auto Read() {
for (std::vector<unsigned char> result;;) {
char buffer[2048];
if (auto read = recv(s, buffer, sizeof buffer, 0); read == 0)
return result;
else if (read == SOCKET_ERROR) {
if (auto lastError = WSAGetLastError(); lastError != WSAEWOULDBLOCK)
throw lastError;
if (!empty(result))
return result;
Sleep(0);
} else
result.insert(end(result), buffer, buffer + read);
}
}
void Write(void* data, int length) {
for (auto p = reinterpret_cast<const char*>(data); 0 < length;) {
auto sent = send(s, p, length, 0);
if (sent == 0)
throw 0;
else if (sent == SOCKET_ERROR)
throw WSAGetLastError();
p += sent;
length -= sent;
}
}
};
int main() {
WSA wsa;
Credential credential;
for (int i = 0; i < 5; i++) {
Socket socket{ "127.0.0.1", 4433 };
std::vector<unsigned char> read;
auto first = true;
CtxtHandle context;
for (SECURITY_STATUS ss = SEC_I_CONTINUE_NEEDED; ss == SEC_I_CONTINUE_NEEDED;) {
SecBuffer inbuf[] = {
{ .BufferType = SECBUFFER_EMPTY },
{ .BufferType = SECBUFFER_EMPTY },
};
if (!first) {
auto data = socket.Read();
read.insert(end(read), begin(data), end(data));
inbuf[0] = { static_cast<unsigned long>(read.size()), SECBUFFER_TOKEN, read.data() };
}
SecBufferDesc indesc = { SECBUFFER_VERSION, 2, inbuf };
SecBuffer outbuf = { .BufferType = SECBUFFER_TOKEN };
SecBufferDesc outdesc = { SECBUFFER_VERSION, 1, &outbuf };
unsigned long attr = 0;
ss = InitializeSecurityContextW(&credential, first ? nullptr : &context, L"localhost", ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NETWORK_DREP, &indesc, 0, &context, &outdesc, &attr, nullptr);
if (FAILED(ss))
throw ss;
first = false;
read.erase(begin(read), end(read) - (inbuf[1].BufferType == SECBUFFER_EXTRA ? inbuf[1].cbBuffer : 0));
if (outbuf.cbBuffer != 0) {
socket.Write(outbuf.pvBuffer, outbuf.cbBuffer);
FreeContextBuffer(outbuf.pvBuffer);
}
}
for (;;) {
SecBuffer buffer[] = {
{ static_cast<unsigned long>(read.size()), SECBUFFER_DATA, read.data() },
{ .BufferType = SECBUFFER_EMPTY },
{ .BufferType = SECBUFFER_EMPTY },
{ .BufferType = SECBUFFER_EMPTY },
};
SecBufferDesc desc{ SECBUFFER_VERSION, 4, buffer };
if (auto ss = DecryptMessage(&context, &desc, 0, nullptr); ss == SEC_I_CONTEXT_EXPIRED)
break;
else if (ss == SEC_E_OK) {
if (buffer[1].BufferType == SECBUFFER_DATA && 0 < buffer[1].cbBuffer && buffer[1].pvBuffer)
printf("%.*s", buffer[1].cbBuffer, reinterpret_cast<const char*>(buffer[1].pvBuffer));
read.erase(begin(read), end(read) - (buffer[3].BufferType == SECBUFFER_EXTRA ? buffer[3].cbBuffer : 0));
} else if (ss != SEC_E_INCOMPLETE_MESSAGE)
throw ss;
if (auto data = socket.Read(); empty(data))
break;
else
read.insert(end(read), begin(data), end(data));
}
if (auto ss = DeleteSecurityContext(&context); ss != SEC_E_OK)
throw ss;
}
}
I'm a maintainer of ftp client. Some ftps server requires that DATA connection must reuse the TLS session of CONTROL connection for security.
At Windows Update 2019/10, RFC7627 Extended Master Secret was enabled. SChannel requires RFC7627 EMS support when RFC5077 TLS Session Resumption.
OpenSSL suport RFC7627 extended master secret from 1.1.0. So SChannel cannot reuse TLS session with OpenSSL 1.0.2.

How to resolve HTTPS status code 404?

I am getting status code 404 for the below code snippet.
Note: please add <HOSTNAME> when you are trying to figure out the issue.
HINTERNET m_hConnect;
HINTERNET m_hSession;
HINTERNET m_hRequest;
m_hSession = WinHttpOpen(userAgent,
WINHTTP_ACCESS_TYPE_NO_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
if (!m_hSession) {
printf("WinHttpOpen:Error %d has occurred.",GetLastError());
return 0;
}
// connect to the IPP server on the given port
m_hConnect = WinHttpConnect( m_hSession, <HOSTNAME>, 443, 0);
if (!m_hConnect) {
printf("WinHttpConnect: Error %d has occurred.",GetLastError());
return 0;
}
// Create an HTTP Request handle.
m_hRequest = WinHttpOpenRequest(m_hConnect, L"POST" ,L"/ipp",
L"HTTP/1.1", WINHTTP_NO_REFERER,
WINHTTP_DEFAULT_ACCEPT_TYPES,WINHTTP_FLAG_SECURE);
if (!m_hRequest) {
printf("WinHttpOpenRequest:Error %d has occurred.",GetLastError());
return 0;
}
DWORD secureflags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID |SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
BOOL bResult= WinHttpSetOption(m_hRequest, WINHTTP_OPTION_SECURITY_FLAGS, (LPVOID)&secureflags, sizeof(secureflags));
if (!bResult) {
printf("WinHttpSetOption:Error %d has occurred.",GetLastError());
return 0;
}
bResult = WinHttpSendRequest(m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS,0,WINHTTP_NO_REQUEST_DATA,0,0,0);
if (!bResult) {
printf("WinHttpSendRequest:Error %d has occurred.",GetLastError());
return 0;
}
if( !WinHttpReceiveResponse( m_hRequest, NULL ) )
{
if( GetLastError( ) == ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED )
{
HCERTSTORE hMyStore = 0;
PCCERT_CONTEXT pCertContext = 0;
WCHAR szCertName[256]={0};
//MY is the store the certificate is in.
hMyStore = CertOpenSystemStore( 0, TEXT("MY") );
if( hMyStore )
{
pCertContext = CertFindCertificateInStore( hMyStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
(LPVOID) szCertName, //Subject string in the certificate.
NULL );
if( pCertContext )
{
WinHttpSetOption( m_hRequest,
WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
(LPVOID) pCertContext,
sizeof(CERT_CONTEXT) );
CertFreeCertificateContext( pCertContext );
}
CertCloseStore( hMyStore, 0 );
// NOTE: Application should now resend the request.
bResult = WinHttpSendRequest(m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS,0,WINHTTP_NO_REQUEST_DATA,0,0,0);
if (!bResult) {
printf("WinHttpSendRequest:Error %d has occurred.",GetLastError());
return 0;
}
}
}
}
DWORD dwStatusCode = 0;
DWORD dwSize = sizeof(DWORD);
BOOL bResults = true;
bResults = WinHttpQueryHeaders( m_hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwStatusCode, &dwSize, NULL );
Result: I am getting status code of 404. I am not able to figure out why it is happening.
Using WinINet, I have followed the same procedure, I am getting status code of 200, which is success. But my requirement is to use only WinHttp.
This is not a mystery. It is happening because the resource you request was not found.
Either:
The URL you used was incorrect, or
The mapping of URLs to resources at the server isn't correct.

openssl BIO_do_connect returns 0 for ssl

Here's my simple openssl client test case trying to connect to google.com:443.
According to the manual, BIO_do_connect should return 1, 0 or -1.
Google didn't find me anyone for whom it returns 0, which it does for me.
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
int main()
{
SSL_load_error_strings();
SSL_library_init();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
SSL_CTX *p_ssl_ctx = NULL;
SSL *p_ssl = NULL;
BIO * bio = NULL;
int r = 0;
// init ssl context
p_ssl_ctx = SSL_CTX_new(SSLv2_client_method()); /* Create new context */
if (p_ssl_ctx == NULL)
{
ERR_print_errors_fp(stderr);
return 3;
}
const char *store_path = "/etc/ssl/certs/ca-certificates.crt";
r = SSL_CTX_load_verify_locations(p_ssl_ctx, store_path, NULL);
if (r == 0) {
fprintf(stderr, "Unable to load the trust store from %s.\n", store_path);
return 4;
}
bio = BIO_new_ssl_connect(p_ssl_ctx);
if (!bio) {
fprintf(stderr, "no bio \n");
return 5;
}
BIO_get_ssl(bio, &p_ssl);
if (!(p_ssl)) {
fprintf(stderr, "no ssl\n");
return 6;
}
SSL_set_mode(p_ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(bio, "www.google.com:443");
r = BIO_do_connect(bio);
if (r < 1) {
fprintf(stderr, "BIO_new_ssl_connect failed: %lu (0x%lx)\n", r, r);
fprintf(stderr, "Error: %s\n", ERR_reason_error_string(ERR_get_error()));
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
ERR_print_errors_fp(stderr);
perror("bio");
return 7;
}
if (SSL_get_verify_result(p_ssl) != X509_V_OK) {
fprintf(stderr, "Unable to verify connection result.\n");
return 8;
}
return 0;
}
returns:
BIO_new_ssl_connect failed: 0 (0x0)
Error: (null)
error:00000000:lib(0):func(0):reason(0)
bio: Success
so how do i get the actual error out of this?
For getting the last state of your SSL connection in your code you can add something like fprintf(stderr,"p_ssl state: %s\n",SSL_state_string_long(p_ssl));.
More generally I suggest you to add an info callback like this : http://www.openssl.org/docs/ssl/SSL_CTX_set_info_callback.html
For your case, you must replace SSLv2_client_method() by something like TLSv1_client_method().

LDAP Authentication, ldap_sasl_bind_s not working but ldap_simple_bind_s works

I have a problem where in ldap_sasl_bind_s does not work, but ldap_simple_bind_s works.
The strange thing is, ldap_sasl_bind_s works even with wrong passwords and gives user the feeling that he has entered a correct password.
PFA code snippet of the problem and suggest me if anything is wrong with my approach.
{
int rc, aReturnVal = 0;
NSString *aUserDN = [NSString stringWithFormat:#"uid=%s,cn=users,dc=example,dc=com", username];
char* userDN = (char*)[aUserDN UTF8String];
rc = ldap_simple_bind_s (
ld,
userDN,
password
);
// TODO: ldap_simple_bind_s is a deprecated method and should not be used for long. ldap_sasl_bind_s is the right method, but is not working for now.
// Find the reason and get this code up and running.
// struct berval *servcred;
// struct berval cred;
// cred.bv_val = password; // my password
// cred.bv_len = strlen(password);
// rc = ldap_sasl_bind_s (
// ld,
// userDN,
// "DIGEST-MD5",
// &cred,
// NULL,
// NULL,
// &servcred
// );
if ( rc != LDAP_SUCCESS ) {
fprintf( stderr, "ldap_sasl_bind: %s\n", ldap_err2string( rc ) );
} else {
aReturnVal = 1;
}
return aReturnVal;
}
I have initialized the LDAP using following code SNIP:
rc = ldap_initialize(&ld, HOSTNAME);
version = LDAP_VERSION3;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
ldap_set_option( ld, LDAP_OPT_REFERRALS, 0 );
I need to be able to login with correct user name and when user tries to enter wrong user name, ldap should say so.
I have referred to following links and their related links to get to this conclusion:
LDAP - How to check a username/password combination?
How to do password authentication for a user using LDAP?
Digest-MD5 auth is more complicated than just sending a bind DN and password. You'll need to use ldap_sasl_interactive_bind_s and provide a callback so the SASL library can combine your credentials with the server-provided nonce.
This code (adapted from this blog post) works for me against an Active Directory server:
#include <stdio.h>
#include <stdlib.h>
#include <ldap.h>
#include <sasl/sasl.h>
typedef struct
{
char *username;
char *password;
} my_authdata;
int my_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in)
{
my_authdata *auth = (my_authdata *)defaults;
sasl_interact_t *interact = (sasl_interact_t *)in;
if(ld == NULL) return LDAP_PARAM_ERROR;
while(interact->id != SASL_CB_LIST_END)
{
char *dflt = (char *)interact->defresult;
switch(interact->id)
{
case SASL_CB_GETREALM:
dflt = NULL;
break;
case SASL_CB_USER:
case SASL_CB_AUTHNAME:
dflt = auth->username;
break;
case SASL_CB_PASS:
dflt = auth->password;
break;
default:
printf("my_sasl_interact asked for unknown %ld\n",interact->id);
}
interact->result = (dflt && *dflt) ? dflt : (char *)"";
interact->len = strlen((char *)interact->result);
interact++;
}
return LDAP_SUCCESS;
}
int main(int argc, char *argv[])
{
if(argc < 3)
{
fprintf(stderr, "Usage: dmd5-bind [username] [password]\n");
return -1;
}
int rc;
LDAP *ld = NULL;
static my_authdata auth;
auth.username = argv[1];
auth.password = argv[2];
char *sasl_mech = ber_strdup("DIGEST-MD5");
char *ldapuri = ber_strdup("ldap://your.server.name.here");
int protocol = LDAP_VERSION3;
unsigned sasl_flags = LDAP_SASL_QUIET;
char *binddn = NULL;
rc = ldap_initialize(&ld, ldapuri);
if(rc != LDAP_SUCCESS)
{
fprintf(stderr, "ldap_initialize: %s\n", ldap_err2string(rc));
return rc;
}
if(ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &protocol) != LDAP_OPT_SUCCESS)
{
fprintf(stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n", protocol);
return -1;
}
rc = ldap_sasl_interactive_bind_s(ld,
binddn,
sasl_mech,
NULL,
NULL,
sasl_flags,
my_sasl_interact,
&auth);
if(rc != LDAP_SUCCESS)
{
ldap_perror(ld, "ldap_sasl_interactive_bind_s");
ldap_unbind_ext_s(ld, NULL, NULL);
return rc;
}
fprintf(stdout, "Authentication succeeded\n");
rc = ldap_unbind_ext_s(ld, NULL, NULL);
sasl_done();
sasl_client_init(NULL);
return rc;
}