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