#include "mod_ssl.h"
  
/* Giacob: static declaration data and function for ocsp */

#include <lber.h>
#include <ldap.h>
#include "ocsp.h"

typedef struct cert_file_st 
        { 
        char *name;
        int   status;
        int   reason;
        char *time;
        char *this;
        char *next; 
        } CERT_FILE;
    
typedef struct response_st
        {
        CERT_FILE *cert_file;
        int rsp_status;
        } RESPONSE_FILE;
   
typedef struct strtbl_st { int t; char *m; } STRTBL;

static OCSP_RESPONSE *ocsp_response_hook (int id, int  rsp_status, X509* xcert, OCSP_CERTID *certid, OCSP_CERTSTATUS *ocsp_status, char *this, char *next);
static OCSP_REQUEST *ocsp_request_hook (X509* xcert, OCSP_CERTID *certid);
static RESPONSE_FILE *ocsp_ldap_hook(X509 *xs, char *mail);
static void get_entry(LDAP *ld, LDAPMessage *entry, RESPONSE_FILE **response);
static void *ocsp_responder (X509 *xs);

static STRTBL crlReasons[8]= {
        { OCSP_REVOKED_STATUS_UNSPECIFIED, "unspecified" },
        { OCSP_REVOKED_STATUS_KEYCOMPROMISE, "keyCompromise" },
        { OCSP_REVOKED_STATUS_CACOMPROMISE, "cACompromise" },
        { OCSP_REVOKED_STATUS_AFFILIATIONCHANGED, "affiliationChanged" },
        { OCSP_REVOKED_STATUS_SUPERSEDED, "superseded" },
        { OCSP_REVOKED_STATUS_CESSATIONOFOPERATION, "cessationOfOperation" },
        { OCSP_REVOKED_STATUS_CERTIFICATEHOLD, "certificateHold" },
        { OCSP_REVOKED_STATUS_REMOVEFROMCRL, "removeFromCRL" } };

static STRTBL respStatus[6] = {
        { OCSP_RESPONSE_STATUS_SUCCESSFULL, "successfull" },
        { OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, "malformedRequest" },
        { OCSP_RESPONSE_STATUS_INTERNALERROR, "internalError" },
        { OCSP_RESPONSE_STATUS_TRYLATER, "tryLater" },
        { OCSP_RESPONSE_STATUS_SIGREQUIRED, "sigRequired" },
        { OCSP_RESPONSE_STATUS_UNAUTHORIZED, "unauthorized" } };

static STRTBL certStatus[3] = {
        { V_OCSP_CERTSTATUS_GOOD, "good" },
        { V_OCSP_CERTSTATUS_REVOKED, "revoked" },
        { V_OCSP_CERTSTATUS_UNKNOWN, "unknown" } };

static STRTBL respIds[2] = {
        { V_OCSP_RESPID_NAME, "name" },
        { V_OCSP_RESPID_KEY, "key" } };

static BIO *bio_err = NULL; 
static int count;  /* certificate file lenght */
static char certificate[1000];   /*  certificate to verify in DER */
static char ocsp_response[50];
static char *attrs[] = {"mail", "cn", "usercertificate;binary", NULL}; /* attributes to be retrieved */

FILE *fperr; /* ocsp debug file pointer */

static const char *ssl_hook_Fixup_vars[] = {
    "SSL_VERSION_INTERFACE",
    "SSL_VERSION_LIBRARY",
    "SSL_PROTOCOL",
    "SSL_CIPHER",
    "SSL_CIPHER_EXPORT",
    "SSL_CIPHER_USEKEYSIZE",
    "SSL_CIPHER_ALGKEYSIZE",
    "SSL_CLIENT_M_VERSION",
    "SSL_CLIENT_M_SERIAL",
    "SSL_CLIENT_V_START",
    "SSL_CLIENT_V_END",
    "SSL_CLIENT_S_DN",
    "SSL_CLIENT_S_DN_C",
    "SSL_CLIENT_S_DN_SP",
    "SSL_CLIENT_S_DN_L",
    "SSL_CLIENT_S_DN_O",
    "SSL_CLIENT_S_DN_OU",
    "SSL_CLIENT_S_DN_CN",
    "SSL_CLIENT_S_DN_Email",
    "SSL_CLIENT_I_DN",
    "SSL_CLIENT_I_DN_C",
    "SSL_CLIENT_I_DN_SP",
    "SSL_CLIENT_I_DN_L",
    "SSL_CLIENT_I_DN_O",
    "SSL_CLIENT_I_DN_OU",
    "SSL_CLIENT_I_DN_CN",
    "SSL_CLIENT_I_DN_Email",
    "SSL_CLIENT_A_KEY",
    "SSL_CLIENT_A_SIG",
    "SSL_OCSP_LDAP_RESPONSE",  /* Giacob: environment var for ocsp */
    "SSL_SERVER_M_VERSION",
    "SSL_SERVER_M_SERIAL",
    "SSL_SERVER_V_START",
    "SSL_SERVER_V_END",
    "SSL_SERVER_S_DN",
    "SSL_SERVER_S_DN_C",
    "SSL_SERVER_S_DN_SP",
    "SSL_SERVER_S_DN_L",
    "SSL_SERVER_S_DN_O",
    "SSL_SERVER_S_DN_OU",
    "SSL_SERVER_S_DN_CN",
    "SSL_SERVER_S_DN_Email",
    "SSL_SERVER_I_DN",
    "SSL_SERVER_I_DN_C",
    "SSL_SERVER_I_DN_SP",
    "SSL_SERVER_I_DN_L",
    "SSL_SERVER_I_DN_O",
    "SSL_SERVER_I_DN_OU",
    "SSL_SERVER_I_DN_CN",
    "SSL_SERVER_I_DN_Email",
    "SSL_SERVER_A_KEY",
    "SSL_SERVER_A_SIG",
    NULL
};

/* Giacob: retrieve cert status from ldap */
   
static RESPONSE_FILE *ocsp_ldap_hook(X509 *xs, char *mail)
/* static char* ocsp_ldap_hook(request_rec *r) */
{ 
   RESPONSE_FILE *response; 
   STRTBL *table;
   LDAP *ld;
   LDAPMessage *res,*e;
   BIO *bp_cert;
   
   char filtpattern[1024], time[15];
   char *ldapservers = "/*ldap-idd.comune.modena.it:389*/ callisto.comune.modena.it:3389";
   int done, rc;
   
   FILE *fpcert; /* certificate file pointer */
   
   /* default response: certificate status REVOKED */
   response->rsp_status = OCSP_RESPONSE_STATUS_SUCCESSFULL;
   response->cert_file->status = V_OCSP_CERTSTATUS_REVOKED;
   response->cert_file->reason = OCSP_REVOKED_STATUS_KEYCOMPROMISE;
   strcpy(time, "19990506120000");
   response->cert_file->time = time;
   table = &certStatus[V_OCSP_CERTSTATUS_REVOKED];
   strcpy(ocsp_response, table->m);

   /* bp_cert = BIO_new(BIO_s_mem());
   i2d_X509_bio(bp_cert, xs);
   BIO_gets(bp_cert, certificate, bp_cert->num_read);
   fprintf(fperr, "Certificate der in var %s.\n", certificate);
   fflush(fperr); */

   fpcert = fopen("/tmp/certificato.der", "w");
   i2d_X509_fp(fpcert, xs);
   fclose(fpcert);

   fpcert = fopen("/tmp/certificato.der", "r");
   count = fread(certificate, 1, 999, fpcert);
   fclose(fpcert); 
   
   fprintf(fperr, "Certificate to verify lenght in bytes: %d. \n", count);
   fflush(fperr);
   
   if ((ld = ldap_init(ldapservers, 0)) == NULL )
       {
        fprintf(fperr,"can't initialize ldap support \n");
        fflush(fperr);
        response->rsp_status = OCSP_RESPONSE_STATUS_TRYLATER;
        response->cert_file->status = V_OCSP_CERTSTATUS_UNKNOWN;
        table = &respStatus[OCSP_RESPONSE_STATUS_TRYLATER];
        strcpy(ocsp_response, table->m);
        return response;
       }
   ld -> ld_options = LDAP_OPT_REFERRALS;
   ld -> ld_sizelimit = 0;
   ld -> ld_timelimit = 0;
   ld -> ld_deref = LDAP_DEREF_ALWAYS;
   
 if ((rc = ldap_bind_s (ld, NULL, NULL, LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS)
    {
     fprintf(fperr,"can't bind to LDAP server %s\n",ldap_err2string (rc));
     fflush(fperr);
     response->rsp_status = OCSP_RESPONSE_STATUS_INTERNALERROR;
     response->cert_file->status = V_OCSP_CERTSTATUS_UNKNOWN;
     table = &respStatus[OCSP_RESPONSE_STATUS_INTERNALERROR];
     strcpy(ocsp_response, table->m);
     return response;
    } else {
            fprintf(fperr,"binded to LDAP server!!!\n");
            fflush(fperr);
           }
   
   /* build filter for ldap search by e-mail */
   ldap_build_filter (filtpattern, 1024, "(%a=%v)", NULL, NULL, "mail", mail, NULL);
   
   if (ldap_search(ld, "o=giacob ,c=it", LDAP_SCOPE_SUBTREE, filtpattern, attrs, 0 ) == -1 )
      {
       fprintf(fperr,"User %s NOT found in LDAP\n", mail);
       fflush(fperr);
       response->rsp_status = OCSP_RESPONSE_STATUS_UNAUTHORIZED;
       response->cert_file->status = V_OCSP_CERTSTATUS_UNKNOWN;
       table = &respStatus[OCSP_RESPONSE_STATUS_UNAUTHORIZED];
       strcpy(ocsp_response, table->m);
       return response;
      } else {
              fprintf(fperr,"user %s found in LDAP\n", mail);
              fflush(fperr);
             }
   
   done = 0;
   
   /* retrieve results of ldap search: must be just one entry */
   while ((rc = ldap_result(ld, LDAP_RES_ANY, 0, NULL, &res)) == LDAP_RES_SEARCH_ENTRY)
          {
           e = ldap_first_entry(ld, res);
           get_entry(ld, e, &response);
           fprintf(fperr,"loop:\n");
           fflush(fperr);
           ldap_msgfree(res);
           done = 1;
          }
   
   if (!done) {
               fprintf(fperr,"Ldap search result not retrieved: %s.\n", ldap_err2string (rc));
               fflush(fperr);
               response->rsp_status = OCSP_RESPONSE_STATUS_INTERNALERROR;
               response->cert_file->status = V_OCSP_CERTSTATUS_UNKNOWN;
               table = &respStatus[OCSP_RESPONSE_STATUS_INTERNALERROR];
               strcpy(ocsp_response, table->m);
               return response;
              }
   
   ldap_msgfree(res);
   ldap_unbind(ld);
   
   fprintf(fperr,"Risposta ocsp: %s.\n", ocsp_response);
   fflush(fperr);
   
   return response;
}  

static void get_entry (LDAP *ld, LDAPMessage *entry, RESPONSE_FILE **response)
   {
    char *attribute;
    char *value;
    BerElement *ber;
    struct berval **bvals;
    int k;
    STRTBL *table;
   
    /* retrieve attributes of the entry found by ldap search */
    for (attribute = ldap_first_attribute(ld, entry, &ber); attribute != NULL; attribute = ldap_next_attribute(ld, entry, ber))
        {
         if ((bvals = ldap_get_values_len(ld, entry, attribute)) != NULL)
             {
              /* get each attribute value of current entry */
              for (k = 0; bvals[k] != NULL; k++)
                  {
                   value = bvals[k]->bv_val;
                   if (!strcmp(attribute, "usercertificate;binary"))
                      {
                       int cert_len = bvals[k]->bv_len;
                       fprintf(fperr,"Certificato trovato in LDAP lungo %d caratteri:\n", cert_len);
                       fflush(fperr);
   
                       /* value, cert_len = certificato corrente LDAP in DER e lunghezza in byte */
                       /* certificate, count = certificato da testare */
                       if (cert_len == count)
                          {
                           /* verify certificate found in LDAP */
                           char *lc;
                           char *sc;
                           sc = certificate;
                           lc = value;
                           while (lc < value + cert_len && *lc++ == *sc++); /* NULL INSTRUCTION */
   
                           if (lc == value + cert_len)   /* certificate found ! */
                              {
                               (*response)->rsp_status = OCSP_RESPONSE_STATUS_SUCCESSFULL;
                               (*response)->cert_file->status = V_OCSP_CERTSTATUS_GOOD;
                               table = &certStatus[V_OCSP_CERTSTATUS_GOOD];
                               strcpy(ocsp_response, table->m);
                              }
                          }
                      }
                    else {
                          ; /* NULL INSTRUCTION. Nothing on other attributes retrieved:
                               mail, cn
                               was: if (!strcmp(attribute, "cn")) strcpy(nome,value);
                                    printf("%s=%s\n", attribute, value);
                            */
                         }
                   }
   
              }
         ber_bvecfree(bvals);  /* frees array of berval returned from search */
  }
 return;
}

/* Giacob: creating ocsp request on certificate */

static OCSP_REQUEST *ocsp_request_hook (X509 *xcert, OCSP_CERTID *certid)
{
 OCSP_REQUEST *req = NULL;
 X509_NAME *xn = NULL;
 BIO *bio = NULL;
 STACK *extns = NULL;
 FILE *fpocsp;

 xn = X509_get_subject_name(xcert);

 if (!(req = OCSP_request_new(xn, extns))) 
    {
     fprintf(fperr,"Can't create OCSP request.\n");
     fflush(fperr);
     return NULL;
    } 
    else {
          fprintf(fperr,"OCSP request created.\n");
          fflush(fperr);
         }   

 if (OCSP_request_add(req, certid, NULL) == 0)
    {
     fprintf(fperr,"Can't put data in OCSP request.\n");                            
     fflush(fperr);
     if (req) OCSP_REQUEST_free(req);
     return NULL; 
    }
    else {
          fprintf(fperr,"OCSP request filled with data.\n");
          fflush(fperr);
         }

 fpocsp = fopen ("/tmp/ocsp-patch.log", "w");
 if ((bio = BIO_new(BIO_s_file())) != NULL) BIO_set_fp(bio, fpocsp, BIO_NOCLOSE);
 OCSP_REQUEST_print(bio, req);
 fflush(fpocsp);
 fclose(fpocsp);

 return req;
}

/* Giacob: creating ocsp response on certificate */
    
static OCSP_RESPONSE *ocsp_response_hook (int id, int rsp_status, X509 *xcert, OCSP_CERTID *certid, OCSP_CERTSTATUS *ocsp_status, char *this, char *next)
{   
 OCSP_BASICRESP *brsp = NULL;
 OCSP_RESPONSE *rsp = NULL;
 X509_NAME* xn;
 BIO *bio;
 FILE *fpocsp;
    
 xn = X509_get_subject_name(xcert);

 if (!(brsp = OCSP_basic_response_new(id, xcert, NULL)))
    { 
     fprintf(fperr,"Cant't create OCSP basic response.\n");
     fflush(fperr);
     return NULL;
    }
    else {
          fprintf(fperr,"OCSP basic response created.\n");
          fflush(fperr);
         }

 if (!(OCSP_basic_response_add(brsp, certid, ocsp_status, this, next, NULL)))
    {
     fprintf(fperr,"Can't put data in OCSP basic response.\n");
     fflush(fperr);
     if (brsp) OCSP_BASICRESP_free(brsp);
     return NULL;
    }
    else {
          fprintf(fperr,"OCSP basic response filled with data.\n");  
          fflush(fperr);
         }
 
 /* OCSP_basic_response_sign(brsp, EVP_PKEY *key, EVP_MD *dgst, STACK *certs); */

 if (!(rsp = OCSP_response_new(rsp_status, NID_id_pkix_ocsp_basic, i2d_OCSP_BASICRESP, (char*)brsp)))
    {
     fprintf(fperr,"Can't create OCSP response.\n");
     fflush(fperr);
     if (brsp) OCSP_BASICRESP_free(brsp);
     return NULL;
    }
    else {
          fprintf(fperr,"OCSP response created.\n");
          fflush(fperr);
         }

 fpocsp = fopen ("/tmp/ocsp-patch.log", "a");
 if ((bio = BIO_new(BIO_s_file())) != NULL) BIO_set_fp(bio, fpocsp, BIO_NOCLOSE);
 OCSP_RESPONSE_print(bio, rsp);
 fflush(fpocsp);
 fclose(fpocsp);

 if (brsp) OCSP_BASICRESP_free(brsp);

 return rsp;
} 

/* Giacob: OCSP responder */

static void *ocsp_responder (X509 *xs)  
{
 OCSP_CERTID *new_cid;
 OCSP_REQUEST *new_req;
 OCSP_RESPONSE *new_rsp;
 OCSP_CERTSTATUS *cert_status = NULL;
 X509_NAME *xn_subject, *xn_issuer;
 RESPONSE_FILE *new_response;

 char name[30], mail[30], time[15], this[15], next[15];

 xn_issuer = X509_get_issuer_name(xs);
 xn_subject = X509_get_subject_name(xs);

 fperr = fopen("/tmp/ocsp-err.log", "w");

 if ((X509_NAME_cmp (xn_issuer, xn_subject)) < 0 )  
    {
     fprintf(fperr,"Subject certificate.\n");
     X509_print_fp(fperr, xs);
     fflush(fperr);
 
     if ((bio_err=BIO_new(BIO_s_file())) != NULL) BIO_set_fp(bio_err, stderr, BIO_NOCLOSE);

     if (!(new_cid = OCSP_cert_id_new(EVP_md5(), X509_get_issuer_name(xs), xs->cert_info->key->public_key, X509_get_serialNumber(xs))))
        {
         fprintf(fperr,"Can't create certificate id.\n");
         fflush(fperr);
         return;
        }
        else {
              fprintf(fperr,"New certificate id created.\n");
              fflush(fperr);
             }

     if (!(new_req = ocsp_request_hook (xs, new_cid))) return;

     X509_NAME_get_text_by_NID(xn_subject, NID_commonName, name, 30);
     X509_NAME_get_text_by_NID(xn_subject, NID_pkcs9_emailAddress, mail, 30);

     strcpy(time, "19990501224320");
     strcpy(this, "19990401224320");
     strcpy(next, "19990601224320");
     fprintf(fperr,"Values inside vars: subject name=%s, subject e-mail=%s.\n", name, mail);
     fflush(fperr);

     if (!(new_response = ocsp_ldap_hook(xs, mail)))
        {
         fprintf(fperr,"Can't get status from LDAP directory.\n");
         fflush(fperr);
         return;
        }
        else {
              fprintf(fperr,"Ldap get status done.\n");
              fflush(fperr);
             }

     new_response->cert_file->name = name;
     new_response->cert_file->time = time;
     new_response->cert_file->this = this;
     new_response->cert_file->next = next;
 
     if (!(cert_status = OCSP_cert_status_new(new_response->cert_file->status, new_response->cert_file->reason, time)))
        {
         fprintf(fperr,"Can't create new certificate status.\n");
         fflush(fperr);
         return;
        }
        else {
              fprintf(fperr,"New certificate status created.\n");       
              fflush(fperr);
             }

     /* if status = revoked ssl_log(s, SSL_LOG_ERROR, "Certificate Status Verification: Revoked Certificate");
      ap_ctx_set(conn->client->ctx, "ssl::verify::error",
             X509_verify_cert_error_string(X509_V_ERR_CERT_REVOKED));
      ok = FALSE; */
 
     if (!(new_rsp = ocsp_response_hook (V_OCSP_RESPID_NAME, new_response->rsp_status, xs, new_cid, cert_status, this, next))) return;

     if (cert_status) OCSP_CERTSTATUS_free(cert_status);

     ERR_print_errors(bio_err);
     ERR_print_errors_fp(stderr);
    }

 fflush(fperr);
 fclose(fperr);

 return;
}
int ssl_hook_Fixup(request_rec *r)
{
    SSLSrvConfigRec *sc = mySrvConfig(r->server);
    SSLDirConfigRec *dc = myDirConfig(r);
    table *e = r->subprocess_env;
    char *var;
    char *val;
    int i;

    /*
     * Check to see if SSL is on
     */
    if (!sc->bEnabled)
        return DECLINED;
    if (ap_ctx_get(r->connection->client->ctx, "ssl") == NULL)
        return DECLINED;

    /*
     * Annotate the SSI/CGI environment with standard SSL information
     */
    ap_table_set(e, "HTTPS", "on"); /* the HTTPS (=HTTP over SSL) flag! */
    for (i = 0; ssl_hook_Fixup_vars[i] != NULL; i++) {
        var = (char *)ssl_hook_Fixup_vars[i];
        val = ssl_var_lookup(r->pool, r->server, r->connection, r, var);
        if (!strIsEmpty(val))
            ap_table_set(e, var, val);
    }

    /* Giacob: set environment ocsp var */
    ap_table_set(e, "SSL_OCSP_LDAP_RESPONSE", ocsp_response);

    /*
     * On-demand bloat up the SSI/CGI environment with certificate data
     */
    if (dc->nOptions & SSL_OPT_EXPORTCERTDATA) {
        val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CLIENT_CERT");
        ap_table_set(e, "SSL_CLIENT_CERT", val);
        val = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_SERVER_CERT");
        ap_table_set(e, "SSL_SERVER_CERT", val);
    }

    /*
     * On-demand bloat up the SSI/CGI environment with compat variables
     */
#ifdef SSL_COMPAT
    if (dc->nOptions & SSL_OPT_COMPATENVVARS)
        ssl_compat_variables(r);
#endif

    return DECLINED;
}
int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
{
    SSL *ssl;
    conn_rec *conn;
    server_rec *s;
    request_rec *r;
    SSLSrvConfigRec *sc;
    SSLDirConfigRec *dc;
    X509 *xs;
    int errnum;
    int errdepth;
    char *cp;
    char *cp2;
    int depth;

    /*
     * Get Apache context back through OpenSSL context
     */
    ssl  = (SSL *)X509_STORE_CTX_get_app_data(ctx);
    conn = (conn_rec *)SSL_get_app_data(ssl);
    r    = (request_rec *)SSL_get_app_data2(ssl);
    s    = conn->server;
    sc   = mySrvConfig(s);
    dc   = (r != NULL ? myDirConfig(r) : NULL);

    /*
     * Get verify ingredients
     */
    xs       = X509_STORE_CTX_get_current_cert(ctx);
    errnum   = X509_STORE_CTX_get_error(ctx);
    errdepth = X509_STORE_CTX_get_error_depth(ctx);

    /*
     * Log verification information
     */
    cp  = X509_NAME_oneline(X509_get_subject_name(xs), NULL, 0);
    cp2 = X509_NAME_oneline(X509_get_issuer_name(xs),  NULL, 0);
    ssl_log(s, SSL_LOG_TRACE,
            "Certificate Verification: depth: %d, subject: %s, issuer: %s",
            errdepth, cp != NULL ? cp : "-unknown-",
            cp2 != NULL ? cp2 : "-unknown");
    if (cp)
        free(cp);
    if (cp2)
        free(cp2);

    /*
     * Check for optionally acceptable non-verifiable issuer situation
     */
    if (   (   errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
            || errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
            || errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
            || errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE  )
        && sc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA            ) {
        ssl_log(s, SSL_LOG_TRACE,
                "Certificate Verification: Verifiable Issuer is configured as "
                "optional, therefore we're accepting the certificate");
        ok = TRUE;
    }

    /*
     * If we already know it's not ok, log the real reason
     */
    if (!ok) {
        ssl_log(s, SSL_LOG_ERROR, "Certificate Verification: Error (%d): %s",
                errnum, X509_verify_cert_error_string(errnum));
        ap_ctx_set(conn->client->ctx, "ssl::client::dn", NULL);
        ap_ctx_set(conn->client->ctx, "ssl::verify::error",
                   X509_verify_cert_error_string(errnum));
    }

    /*
     * Finally check the depth of the certificate verification
     */
    if (dc != NULL && dc->nVerifyDepth != UNSET)
        depth = dc->nVerifyDepth;
    else 
        depth = sc->nVerifyDepth;
    if (errdepth > depth) {
        ssl_log(s, SSL_LOG_ERROR,
                "Certificate Verification: Certificate Chain too long "
                "(chain has %d certificates, but maximum allowed are only %d)", 
                errdepth, depth);
        ap_ctx_set(conn->client->ctx, "ssl::verify::error",
                   X509_verify_cert_error_string(X509_V_ERR_CERT_CHAIN_TOO_LONG));
        ok = FALSE;
    }

    /* Giacob: take certificate for ldap_hook */
    ocsp_responder (xs);

    /*
     * And finally signal OpenSSL the (perhaps changed) state
     */
    return (ok);
}