During the test of my C code using OpenSSL, I noticed that even though
CA certs were not loaded, SSL verification succeeded unexpectedly.
Attached below is a simplified code that demonstrates what I have been
seeing. I intentionally commented out the section where tries to load
CA certs, but SSL_get_verify_results() returns X509_V_OK where I
expects X509_V_ERR_CERT_UNTRUSTED to return. Looking at my log, the
verifyCallback() detected the error, returning 0 to the caller but the
return value from SSL_get_verify_result() did not seem to reflect the
error detected in the verifyCallback().
Here's some tty logs:
(1) When CA certs are not loaded:
TCP connection successful
>>>> verifyCallback() - in: preverify_ok=0
Verify error: unable to get local issuer certificate(20)
- depth=1
- sub ="/C=US/O=Google Inc/CN=Google Internet Authority"
<<<< verifyCallback() - out
SSL handshake/verify successful
PASS
(2) When CA certs are loaded:
TCP connection successful
>>>> verifyCallback() - in: preverify_ok=1
<<<< verifyCallback() - out
>>>> verifyCallback() - in: preverify_ok=1
<<<< verifyCallback() - out
>>>> verifyCallback() - in: preverify_ok=1
<<<< verifyCallback() - out
SSL handshake/verify successful
PASS
(3) When CA certs are NOT loaded, and returning 1 always from verifyCallback()
TCP connection successful
>>>> verifyCallback() - in: preverify_ok=0
Verify error: unable to get local issuer certificate(20)
- depth=1
- sub ="/C=US/O=Google Inc/CN=Google Internet Authority"
<<<< verifyCallback() - out
>>>> verifyCallback() - in: preverify_ok=0
Verify error: certificate not trusted(27)
- depth=1
- sub ="/C=US/O=Google Inc/CN=Google Internet Authority"
<<<< verifyCallback() - out
>>>> verifyCallback() - in: preverify_ok=1
<<<< verifyCallback() - out
SSL verify failed: CERT_UNTRUSTED(27)
FAIL
As in (3), if 1 is always returned from verifyCallback(),
SSL_get_verify_result() seems to return the expected error.
So, my question is, in order to correctly detect 'CERT_UNTRUSTED'
error in the code, what needs to be done in the implementation? There
may be something I am doing right. Please let me know if you notice
anything.
Here's info of OpenSSL I am using:
OpenSSL 0.9.8r 8 Feb 2011
compiler: -arch x86_64 -fmessage-length=0 -pipe -Wno-trigraphs
-fpascal-strings -fasm-blocks -O3 -D_REENTRANT -DDSO_DLFCN
-DHAVE_DLFCN_H -DL_ENDIAN -DMD32_REG_T=int -DOPENSSL_NO_IDEA
-DOPENSSL_PIC -DOPENSSL_THREADS -DZLIB -mmacosx-version-min=10.6
built on: Apr 22 2011
platform: darwin64-x86_64-llvm
OPENSSLDIR: "/System/Library/OpenSSL"
Any comments are appreciated!!
- Yutaka
/* ssltest.c */
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <stdio.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#define HOST "encrypted.google.com"
#define PORT (443)
#define MAX_VERIFY_DEPTH (2)
#define CA_CERT_PATH "./ca-bundle.crt"
static char const* ssl_strerror(SSL* ssl, int ret);
static char const* crt_strerror(int err);
int verifyCallback(int preverify_ok, X509_STORE_CTX *ctx)
{
fprintf(stdout, ">>>> verifyCallback() - in: preverify_ok=%d\n",
preverify_ok);
if(!preverify_ok)
{
char buf[256];
X509 *err_cert;
int err, depth;
SSL *ssl;
err_cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
fprintf(stderr, "Verify error: %s(%d)\n",
X509_verify_cert_error_string(err), err);
fprintf(stderr, " - depth=%d\n", depth);
fprintf(stderr, " - sub =\"%s\"\n", buf);
}
fprintf(stdout, "<<<< verifyCallback() - out\n");
//return 1;
return preverify_ok;
}
int connectTcp()
{
struct hostent *h;
struct sockaddr_in sin;
int fd = -1;
int ret;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
/* Resolve remote IP address */
h = gethostbyname(HOST);
if(!h)
{
fprintf(stderr, "Could not obtain IP address\n");
return -1;
}
sin.sin_addr = *(struct in_addr*)(h->h_addr_list[0]);
/* Create fd */
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0)
{
return -1;
}
/* Connect to remote */
ret = connect(fd, (struct sockaddr*)&sin, sizeof(sin));
if(ret < 0)
{
close(fd);
return -1;
}
return fd; /* connected */
}
int test(void)
{
int ret = 0;
SSL_CTX *ctx = 0;
SSL *ssl = 0;
int fd = -1;
/* Create SSL_CTX */
ctx = SSL_CTX_new(SSLv3_client_method());
if(!ctx)
{
fprintf(stderr, "SSL_CTX_new filed");
ret = -1;
goto bail;
}
#if 0 /* Intentionally commented out not to load CA certs. */
/* Load CA certs from file */
if(!SSL_CTX_load_verify_locations(ctx, CA_CERT_PATH, NULL))
{
fprintf(stderr, "Error setting certificate verify locations\n");
ret = -1;
goto bail;
}
#endif
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verifyCallback);
SSL_CTX_set_verify_depth(ctx, MAX_VERIFY_DEPTH + 1);
fd = connectTcp();
if(fd < 0)
{
fprintf(stderr, "TCP connection failed\n");
ret = -1;
goto bail;
}
fprintf(stdout, "TCP connection successful\n");
/* Create SSL */
ssl = SSL_new(ctx);
if(!ssl)
{
fprintf(stderr, "Could not create SSL object");
goto bail;
}
/* Bind socket with the ssl */
ret = SSL_set_fd(ssl, fd);
if(!ret)
{
fprintf(stderr, "SSL_set_fd() failed: %s\n",
ERR_error_string(ERR_get_error(), NULL));
ret = -1;
goto bail;
}
/* Set SSL to connect state */
ret = SSL_connect(ssl);
if(ret != 1)
{
fprintf(stderr, "SSL handshake failed: %s", ssl_strerror(ssl, ret));
ret = -1;
goto bail;
}
ret = SSL_get_verify_result(ssl);
if(ret != X509_V_OK)
{
printf("SSL verify failed: %s(%d)\n", crt_strerror(ret), ret);
ret = -1;
goto bail;
}
fprintf(stdout, "SSL handshake/verify successful\n");
bail:
if(ssl)
{
SSL_shutdown(ssl);
SSL_free(ssl);
}
if(fd >= 0)
{
shutdown(fd, SHUT_WR);
close(fd);
}
if(ctx)
{
SSL_CTX_free(ctx);
}
return ret;
}
int main(void)
{
int ret;
/* Init OpenSSL ... */
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
/* Dump what I am using ... */
fprintf(stdout, "%s\n", SSLeay_version(SSLEAY_VERSION));
fprintf(stdout, "%s\n", SSLeay_version(SSLEAY_CFLAGS));
fprintf(stdout, "%s\n", SSLeay_version(SSLEAY_BUILT_ON));
fprintf(stdout, "%s\n", SSLeay_version(SSLEAY_PLATFORM));
fprintf(stdout, "%s\n", SSLeay_version(SSLEAY_DIR));
ret = test();
if(ret < 0)
{
fprintf(stderr, "FAIL\n");
}
else
{
fprintf(stdout, "PASS\n");
}
return ret;
}
char const* ssl_strerror(SSL* ssl, int ret)
{
int err = SSL_get_error(ssl, ret);
switch(err)
{
case SSL_ERROR_NONE:
return "SSL_ERROR_NONE";
case SSL_ERROR_ZERO_RETURN:
return "SSL_ERROR_ZERO_RETURN";
case SSL_ERROR_WANT_READ:
return "SSL_ERROR_WANT_READ";
case SSL_ERROR_WANT_WRITE:
return "SSL_ERROR_WANT_WRITE";
case SSL_ERROR_WANT_CONNECT:
return "SSL_ERROR_WANT_CONNECT";
case SSL_ERROR_WANT_ACCEPT:
return "SSL_ERROR_WANT_ACCEPT";
case SSL_ERROR_WANT_X509_LOOKUP:
return "SSL_ERROR_WANT_X509_LOOKUP";
case SSL_ERROR_SYSCALL:
return "SSL_ERROR_SYSCALL";
case SSL_ERROR_SSL:
return "SSL_ERROR_SSL";
}
return "Unknown SSL error";
}
char const* crt_strerror(int err)
{
switch(err)
{
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
return "UNABLE_TO_DECRYPT_CERT_SIGNATURE";
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
return "UNABLE_TO_DECRYPT_CRL_SIGNATURE";
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
return "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY";
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
return "CERT_SIGNATURE_FAILURE";
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
return "CRL_SIGNATURE_FAILURE";
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
return "ERROR_IN_CERT_NOT_BEFORE_FIELD";
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
return "ERROR_IN_CERT_NOT_AFTER_FIELD";
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
return "ERROR_IN_CRL_LAST_UPDATE_FIELD";
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
return "ERROR_IN_CRL_NEXT_UPDATE_FIELD";
case X509_V_ERR_CERT_NOT_YET_VALID:
return "CERT_NOT_YET_VALID";
case X509_V_ERR_CERT_HAS_EXPIRED:
return "CERT_HAS_EXPIRED";
case X509_V_ERR_OUT_OF_MEM:
return "OUT_OF_MEM";
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
return "UNABLE_TO_GET_ISSUER_CERT";
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
return "UNABLE_TO_GET_ISSUER_CERT_LOCALLY";
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
return "UNABLE_TO_VERIFY_LEAF_SIGNATURE";
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
return "DEPTH_ZERO_SELF_SIGNED_CERT";
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
return "SELF_SIGNED_CERT_IN_CHAIN";
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
return "CERT_CHAIN_TOO_LONG";
case X509_V_ERR_CERT_REVOKED:
return "CERT_REVOKED";
case X509_V_ERR_INVALID_CA:
return "INVALID_CA";
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
return "PATH_LENGTH_EXCEEDED";
case X509_V_ERR_INVALID_PURPOSE:
return "INVALID_PURPOSE";
case X509_V_ERR_CERT_UNTRUSTED:
return "CERT_UNTRUSTED";
case X509_V_ERR_CERT_REJECTED:
return "CERT_REJECTED";
case X509_V_ERR_UNABLE_TO_GET_CRL:
return "UNABLE_TO_GET_CRL";
case X509_V_ERR_CRL_NOT_YET_VALID:
return "CRL_NOT_YET_VALID";
case X509_V_ERR_CRL_HAS_EXPIRED:
return "CRL_HAS_EXPIRED";
}
return "Unknown verify error";
}
______________________________________________________________________
OpenSSL Project http://www.openssl.org
User Support Mailing List [email protected]
Automated List Manager [email protected]