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                    openssl-users@openssl.org
Automated List Manager                           majord...@openssl.org

Reply via email to