Hi!
I'm using openssl 0.9.3a running under Windows NT4 (SP4), compiling
with MS VC++ 6.0. I'm trying to understand how the library works, so
I've put together some code that just tries to connect to a www-server
talking SSL and try to issue a "GET / HTTP/1.0\n\n" and see what gets
back. Now, I've run in to some (probably newbie-) problems. I've read
all the documentation on the openssl www-site and I have read the
relevant parts of the docs at
http://www.columbia.edu/~ariel/ssleay/index.html
Full source code (.cpp) is at the bottom of the message, if I'm
missing some initiation of the library. (I apologize for the lengthy
post that is the result of this - but I thought it would be best to
give you experts a complete picture of what I'm doing. Flames should
be sent to my inbox and not to the list).
Btw, if I don't try to provoke an error, everything works - the server
sends back the expected data. But I would like to be able to handle
errors too - user's tend to be very unhappy about programs that justs
barfs some strange error message and abort()s. :)
Ok. here goes:
* Setting of verify callback.
if I do this:
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE,&verify_callback);
sslcon = SSL_new(ctx); // new connection
SSL_set_verify(sslcon, SSL_VERIFY_PEER,&verify_callback);
SSL_set_connect_state(sslcon); // this seem to be needed some times
SSL_set_fd(sslcon, fd); // connect the socket and the ssl conn
err = SSL_connect(sslcon);
My verify-callback gets called but the server isn't verified (the
connect is ok although the issuer's certificate can't be found).
If I omit the SSL_CTX_set_verify(...) the server gets verified (which,
of course fails because the issuer's cert can't be found). However, my
verify callback function doesn't get called!
Conclusion: It seems that a) verify options set in the CTX overrides
anything set in the SSL-struct and b) the verify callback configured
in the SSL doesn't get called (might be the same as a if the pointer
to the callback in the CTX is initally NULL). This seems weird, so I
assume that I'm missing something. Any ideas?
----------------------------------------
* CA-list per SSL?
Is there anyway to tell each SSL where to look for issuer's
certificates when trying to validate the server it's connecting to? On
a SSL_CTX you can do SSL_CTX_load_verify_locations(...), but there
seem to be no such function for a SSL?
The SSL_set_client_CA_list() is (if I understand the previous postings
on the subject) only used to tell the SSL who have issued the
_client's_ certificate which it send to the server (so the server can
verify the client). Or?
----------------------------------------
* ASN1_TIME-structure -> string?
Is there any function to convert a ASN1_TIME-structure to a string? It
seems like it can be printed via ASN1_TIME_print(....), but this only
talks to the BIO-library?
----------------------------------------
* Error reporting?
There seem to be several way to check for errors.
There is the ERR_get_error() (does this clear the error?)
Some of the SSL-functions return <= 0 on error.
There is the SSL_get_error()-function to which I'm supposed to pass
the return value from e.g. SSL_connect() in case that return value is
<=0?
There is a SSL_get_verify_result() which a pretty obvious use.
But how is all this connected? In case there was a problem with the
certificate SSL_connect() returns -1 and both ERR_get_error() and
SSL_get_verify_result gives reasonable answers.
If I change the list of preferred ciphers so that the client and the
server has no common cipher (the server is an export version of MS
IIS) then SSL_connect() returns -1, SSL_get_verify_result() returns 0
(reasonable - there has been no cert verification yet) but
ERR_get_error() returns 0 (isn't that supposed to be the code for
OK?). SSL_get_error() returns 6. Where do I look for a #define to
match that error? (the only thing I can see in ssl.h is
SSL_ERROR_ZERO_RETURN and that seems pretty confusing).
All help will be greatly appreciated.
Regards Jesper Trägårdh
--------------------- source code ------------------------------
/* openSSL header-files - lot's of strange errors if e_os.h isn't
included...
*/
#include <e_os.h>
#include <ssl.h>
#include <err.h>
/* std.library */
#include <stdio.h>
#include <string.h>
/* windows stuff */
#include <windows.h>
#include <winsock.h>
void win32_locking_callback(int mode, int type, const char *file, int
line);
/* locking function used by OpenSSL */
int MS_CALLBACK verify_callback(int ok, X509_STORE_CTX *ctx);
/* this function is called during the peer validation process. This
code
*/
static void MS_CALLBACK client_info_callback(SSL *s, int where, int
ret);
/* can be called by OpenSSL to print debugging information */
static HANDLE lock_cs[CRYPTO_NUM_LOCKS]; /* mutexes used by openSSL
* CRYPTO_NUM_LOCKS is defined
in
* ssl.h */
static int verify_depth; /* how far the certificate chain should
* be traversed - this should really be stored
per
* connection or CTX-basis and not here - but
how
* can we get such a variable from the callback
* function???
*/
static WSADATA WsaData; // winsock data area
int main(int argc, char **argv) {
SSL_CTX *ctx;
int fd;
SSL *sslcon;
const char *ca_pem_fn = "foo.pem";
int err,i;
SOCKADDR_IN remoteAddr;
int nbio;
char *buff; // buffer for various purposes
int bytesread;
err = WSAStartup (0x0101, &WsaData); // Windows
socket init
if (err == SOCKET_ERROR)
{
fprintf (stderr, "WSAStartup Failed\n");
abort();
}
if ((buff = (char *)malloc(8192)) == NULL) {
fprintf(stderr, "Out of memory!\n");
abort();
}
if ((argc > 1) && (strcmp(argv[1],"-nbio") == 0)) {
fprintf(stderr, "Using non-blocking I/O\n");
nbio = 1;
} else {nbio = 0;}
for (i=0; i<CRYPTO_NUM_LOCKS; ++i) {
lock_cs[i] = CreateMutex(NULL, FALSE, NULL);
}
/* set locking function */
CRYPTO_set_locking_callback(&win32_locking_callback);
/* add algoritms to OpenSSL library and other init stuff */
SSL_library_init();
/* load all error strings */
SSL_load_error_strings();
ERR_load_crypto_strings();
/* certificate chains should only be traversed 1 level */
verify_depth = 1;
if ((ctx = SSL_CTX_new(SSLv2_method())) == NULL) {
ERR_error_string(ERR_get_error(), buff);
fprintf(stderr, "SSL_CTX_new: error: %s\n", buff);
}
/* this hairy line stems from the fact that the variable that
holds the callback-function in a SSL_CTX is poorly defined */
SSL_CTX_set_info_callback(ctx,(void (__cdecl
*)(void))&client_info_callback);
SSL_CTX_load_verify_locations(ctx, ca_pem_fn, NULL);
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE,&verify_callback);
/* set up a socket */
fd = socket (AF_INET, SOCK_STREAM, 0);
ZeroMemory(&remoteAddr,sizeof(remoteAddr));
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_port = htons(443);
remoteAddr.sin_addr.s_addr = htonl(2196440345UL); // the servers
ip
err = connect (fd, (PSOCKADDR) &remoteAddr, sizeof(remoteAddr)); //
standard connect
fprintf(stderr, "Back from call to connect() ret %d\n", err);
if (err == SOCKET_ERROR)
{
closesocket (fd);
fprintf(stderr, "connect() failed!\n");
abort();
}
sslcon = SSL_new(ctx); // new connection
if (sslcon == NULL) {
fprintf(stderr,"%s\n",
ERR_reason_error_string(ERR_get_error()));
abort();
}
/* this does not seem to override what we've done in the SSL_CTX
... */
SSL_set_verify(sslcon, SSL_VERIFY_PEER,&verify_callback);
/* uncomment to make sure that the server and the client
wont be on speaking terms. :) */
// SSL_set_cipher_list(sslcon, "RC4-MD5");
SSL_set_connect_state(sslcon); // this seem to be needed some times
SSL_set_fd(sslcon, fd); // connect the socket and the ssl conn
err = SSL_connect(sslcon);
fprintf(stderr, "SSL_connect returned: %d\n", err);
fprintf(stderr, "negotiated cipher: %s\n", SSL_get_cipher(sslcon));
SSL_get_shared_ciphers(sslcon, buff, 2048);
fprintf(stderr, "common ciphers %s\n", buff);
if (err <= 0) {
int err_vfy, err_ssl, err_err;
err_ssl = SSL_get_error(sslcon, err);
err_err = ERR_get_error();
err_vfy = SSL_get_verify_result(sslcon);
fprintf(stderr, "err_ssl = %d , err_err = %d (%s) err_vfy = %d
(%s)\n",
err_ssl, err_err, ERR_reason_error_string(err_err),
err_vfy,
X509_verify_cert_error_string(err_vfy));
}
if (nbio == 1) {
fprintf(stderr, "Enabling read_ahead\n");
SSL_set_read_ahead(sslcon, 1);
fprintf(stderr,"ioctlsocket ret: %d\n", err);
}
/* write a HTTP-request */
sprintf(buff, "GET / HTTP/1.0\n\n");
SSL_write(sslcon, buff, 18);
if (nbio == 1) {
int sslr;
/* wait for something to become available */
do {
fprintf(stderr, "SSL_pending check...\n");
sslr = SSL_pending(sslcon);
fprintf(stderr, "SSL_pending = %d\n", sslr);
if (sslr == 0) {
fprintf(stderr, " nothing to read. sleeping for 2s...");
Sleep(2000);
}
fprintf(stderr, "\n");
} while (i == 0);
}
/* try to read something */
bytesread = SSL_read(sslcon,buff,2048);
fprintf(stderr, "data received: (%d bytes)\n", bytesread);
for(i = 0; i < bytesread; i++) {
/* quick and dirty (and a performance nightmare. :) ) */
fprintf(stderr, "%c", buff[i]);
}
/* clean up sockets */
SSL_shutdown(sslcon);
closesocket(fd);
return 0;
}
/*
=======================================================================
*/
void win32_locking_callback(int mode, int type, const char *file, int
line) {
if (mode & CRYPTO_LOCK) {
WaitForSingleObject(lock_cs[type],INFINITE);
} else {
ReleaseMutex(lock_cs[type]);
}
}
/*
=======================================================================
*/
int MS_CALLBACK verify_callback(int ok, X509_STORE_CTX *ctx) {
/* this code is copied almost verbatim from apps\s_cb.c */
char buf[256]; /* local string buffer */
X509 *err_cert;
int err,depth;
fprintf(stderr, "Enter verify_callback. ok=%u\n",ok);
/* print data here? */
err_cert=X509_STORE_CTX_get_current_cert(ctx);
err= X509_STORE_CTX_get_error(ctx);
depth= X509_STORE_CTX_get_error_depth(ctx);
X509_NAME_oneline(X509_get_subject_name(err_cert),buf,256);
fprintf(stderr,"verify_callback: depth=%d %s\n",depth,buf);
if (!ok) {
fprintf(stderr,"verify_callback: verify error:num=%d:%s\n"
,err,X509_verify_cert_error_string(err));
/* check traversal depth - this is not done by the library? */
if (verify_depth >= depth) {
ok=1;
X509_STORE_CTX_set_error(ctx,X509_V_OK);
fprintf(stderr,"verify_callback: verify_depth>=depth\n");
} else {
ok=0;
X509_STORE_CTX_set_error(ctx,X509_V_ERR_CERT_CHAIN_TOO_LONG);
fprintf(stderr,"verify_callback: verify_depth<depth
returning 0!\n");
}
}
/* print detailed error info - we have already printed the error
number
* and an explaining string. See above */
switch (ctx->error) {
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert),buf,256);
fprintf(stderr,"verify_callback: issuer= %s\n",buf);
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
fprintf(stderr,"notBefore=");
/* there must be a function to make an ordinary string from the
ASN1_TIME-structure... */
/*
ASN1_TIME_print(bio_err,X509_get_notBefore(ctx->current_cert)); */
fprintf(stderr,"<time display unimplemented>\n");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
fprintf(stderr,"notAfter=");
/* there must be a function to make an ordinary string from the
ASN1_TIME-structure... */
/*
ASN1_TIME_print(bio_err,X509_get_notAfter(ctx->current_cert)); */
fprintf(stderr,"<time display unimplemented>\n");
break;
} // switch
fprintf(stderr, "Exit verify_callback. returning %u\n", ok);
return(ok);
}
/*
=======================================================================
*/
static void MS_CALLBACK client_info_callback(SSL *s, int where, int ret)
{
/* we assume that this function is not called unless the state
info is to be printed */
char *str = NULL;
if (where & SSL_CB_LOOP) {
fprintf(stderr,"client_info_callback: SSL state is %s.
ret=%d\n",
SSL_state_string_long(s), ret);
}
else if (where & SSL_CB_ALERT) {
str=(where & SSL_CB_READ)?"read":"write";
fprintf(stderr,"client_info_callback: SSL3 alert: ret = %d.
%s%s\n",
ret, SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
} else if (where & SSL_CB_EXIT) {
if (ret == 0) {
fprintf(stderr,"client_info_callback: ret = 0. failing:
%s\n",
SSL_state_string_long(s));
} else if (ret < 0) {
fprintf(stderr,"client_info_callback: ret = %d. error:
%s\n",
ret,SSL_state_string_long(s));
}
}
return;
}
______________________________________________________________________
OpenSSL Project http://www.openssl.org
User Support Mailing List [EMAIL PROTECTED]
Automated List Manager [EMAIL PROTECTED]