this is a basically a question regarding OpenSSL
on Win32, but it has some implications with
ActivePerl; maybe someone could hint me on how
to debug this sort of thing.
i wrote a Perl module which sends XML messages
over a SSL connection to a remote server. i need to
authenticate the server, so i thought of using
SSL_CTX_set_verify with my own callback.
getting Net::SSLeay to work on Win32 appeared to be quite
painful, so after browsing the ActiveState mailing list
archives i decided to roll my own by writing a few functions
in XS.
then i compiled openssl-0.9.6 with VC++ 6.0 and NASM. it compiled
fine, but i had to disable /WX in ms\ntdll.mak, because
there were hundreds of warnings.
then, after some Makefile-tweaking i managed to compile
the Perl module, which uses XS for some SSL-related
stuff; i attached the relevant functions.
The problem function is init_ssl, it says
SSL_get_peer_cert_chain: error:00000000:lib(0):func(0):reason(0)
debug: freed SSL_CTX
or
SSL_get_peer_certificate: error:00000000:lib(0):func(0):reason(0)
at c:/Perl/site/lib/Netpay.pm line 178.
both functions return NULL on Win32, but
ERR_error_string_n(ERR_get_error, ...) doesn't show
anything. On UNIX, everything works fine,
and when there is an error, ERR_error_string_n will
get it.
disabling certificate verification entirely causes
ActivePerl to segfault/crash/or whatever you call it on Win32 :)
On UNIX, everything works fine.
cu,
--
Toni Andjelkovic
<[EMAIL PROTECTED]>
/*
* "This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
* This product includes cryptographic software written by Eric Young
* ([EMAIL PROTECTED]). This product includes software written by Tim
* Hudson ([EMAIL PROTECTED]).
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <stdio.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
/*
* cert verify callback
*/
static int
verify_callback(int ok, X509_STORE_CTX *ctx)
{
char buf[512];
X509 *err_cert;
int err, depth;
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, 512);
if (!ok) {
warn("verify error:num=%d:%s:depth=%d:%s\n", err,
X509_verify_cert_error_string(err), depth, buf);
}
if (!ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) {
X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 512);
warn("issuer= %s\n", buf);
}
return(ok);
}
/* see typemap */
MODULE = Netpay PACKAGE = Netpay
PROTOTYPES: ENABLE
SSL_CTX *
init_ctx(ca_cert, ca_cert_path, verify_depth)
SV *ca_cert
SV *ca_cert_path
SV *verify_depth
PREINIT:
int _verify_depth = 0;
char *_ca_cert = NULL;
char *_ca_cert_path = NULL;
SSL_CTX *ctx = NULL;
STRLEN len = 0;
int rc = 0;
CODE:
{
#ifdef URANDOM
RAND_load_file("/dev/urandom", 16384);
#else
RAND_seed("foobar", 16384);
#endif
if (SvOK(verify_depth)) {
_verify_depth = (int) SvIV(verify_depth);
}
if (SvOK(ca_cert)) {
_ca_cert = SvPV(ca_cert, len);
}
if (SvOK(ca_cert_path)) {
_ca_cert_path = SvPV(ca_cert_path, len);
}
/* init SSL stuff */
SSL_library_init();
SSL_load_error_strings();
/* alloc SSL_CTX */
/* ctx = SSL_CTX_new(SSLv3_client_method()); */
ctx = SSL_CTX_new(TLSv1_client_method());
if (!ctx) {
warn("SSL_CTX_new: NULL");
ERR_print_errors_fp(stderr);
XSRETURN_UNDEF;
}
/* various bug workarounds */
SSL_CTX_set_options(ctx, SSL_OP_ALL);
/* don't verify if neither certificate path is defined */
if ((_ca_cert != NULL) || (_ca_cert_path != NULL)) {
/* load CA certificate file (PEM-encoded) */
rc = SSL_CTX_load_verify_locations(ctx, _ca_cert, _ca_cert_path);
if (!rc) {
warn("SSL_CTX_load_verify_locations: %d", rc);
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx); /* free it! */
XSRETURN_UNDEF;
}
SSL_CTX_set_verify_depth(ctx, _verify_depth);
/* set verify callback */
SSL_CTX_set_verify(
ctx,
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
verify_callback
);
}
RETVAL = ctx;
}
OUTPUT:
RETVAL
SSL *
init_ssl(ctx, fd, tmout)
SSL_CTX *ctx
int fd
int tmout
PREINIT:
SSL *ssl = NULL;
SSL_SESSION *ses = NULL;
int rc = 0;
STACK_OF(X509) *sk = NULL;
char err[512];
char buf[512];
X509 *peer_crt = NULL;
CODE:
{
/* alloc SSL */
ssl = SSL_new(ctx);
if (!ssl) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_new: %s", err);
XSRETURN_UNDEF;
}
rc = SSL_set_fd(ssl, fd);
if (!rc) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_set_fd: %s", err);
SSL_free(ssl);
XSRETURN_UNDEF;
}
/* go */
rc = SSL_connect(ssl);
if (!rc) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_connect: %s", err);
SSL_free(ssl);
XSRETURN_UNDEF;
}
sk = SSL_get_peer_cert_chain(ssl); /* verifies via SSL_CTX callback */
if (!sk) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_get_peer_cert_chain: %s\n", err);
SSL_free(ssl);
XSRETURN_UNDEF;
}
peer_crt = SSL_get_peer_certificate(ssl);
if (!peer_crt) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_get_peer_certificate: %s", err);
SSL_free(ssl);
XSRETURN_UNDEF;
}
/* print it */
if (SvOK(perl_get_sv("Netpay::VERBOSE", FALSE))) {
warn("debug: Peer : %s\n", X509_NAME_oneline(X509_get_subject_name(peer_crt),
buf, 512));
warn("debug: Issuer: %s\n", X509_NAME_oneline(X509_get_issuer_name(peer_crt),
buf, 512));
}
if (SSL_get_verify_result(ssl) != X509_V_OK) {
ERR_error_string_n(SSL_get_verify_result(ssl), err, 512);
warn("SSL_get_verify_result: %s\n", err);
X509_free(peer_crt);
SSL_free(ssl);
XSRETURN_UNDEF;
}
/* we're authenticated, let's retrieve the session
and set some timeouts */
ses = SSL_get_session(ssl);
if (!ses) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_get_session: %s", err);
X509_free(peer_crt);
SSL_free(ssl);
XSRETURN_UNDEF;
}
rc = SSL_SESSION_set_timeout(ses, (long) tmout);
if (!rc) {
ERR_error_string_n(ERR_get_error(), err, 512);
warn("SSL_SESSION_set_timeout: %s", ssl);
X509_free(peer_crt);
SSL_free(ssl);
XSRETURN_UNDEF;
}
X509_free(peer_crt);
RETVAL = ssl;
}
OUTPUT:
RETVAL
void
ctx_free(ctx)
SSL_CTX *ctx;
PPCODE:
{
/* clean up */
SSL_CTX_free(ctx);
if (SvOK(perl_get_sv("Netpay::VERBOSE", FALSE))) {
warn("debug: freed SSL_CTX\n");
}
XSRETURN_YES; /* return true */
}
void
ssl_free(ssl)
SSL *ssl
PPCODE:
{
/* clean up */
SSL_shutdown(ssl); /* don't care about the retcode :) */
SSL_free(ssl);
if (SvOK(perl_get_sv("Netpay::VERBOSE", FALSE))) {
warn("debug: freed SSL\n");
}
XSRETURN_YES; /* return true */
}