File openca-ocspd-3.1.2.patch of Package ocspd-3.1.2

diff -ur openca-ocspd-3.1.2-orig/configure openca-ocspd-3.1.2/configure
--- openca-ocspd-3.1.2-orig/configure	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/configure	2025-07-07 15:43:09.039284603 +0200
@@ -3891,7 +3891,7 @@
   CFLAGS=$ac_save_CFLAGS
 elif test $ac_cv_prog_cc_g = yes; then
   if test "$GCC" = yes; then
-    CFLAGS="-g -O2"
+    CFLAGS="-g"
   else
     CFLAGS="-g"
   fi
@@ -12062,7 +12062,7 @@
 if [ -f "/etc/issue" ] ; then
    DIST_NAME=`head -n 1 /etc/issue | cut -f 1,1 -d ' '`
 else
-   DIST_NAME=`echo ${build_os} | $EGREP -o [A-Za-z]+`
+   DIST_NAME=`echo ${build_os} | $EGREP -o [A-Za-z]+ | head -n 1`
 fi
 
 
@@ -13083,7 +13083,7 @@
 #libpki_cflags="-I`${libpki_config} --prefix`/include "
 pkicflags="`${libpki_config} --cflags`"
 # pkildlibs="-L`${libpki_config} --prefix`/lib -lpki"
-pkildlibs="`${libpki_config} --libs`"
+pkildlibs="`${libpki_config} --libs` -lmemcached"
 pkiversion=`${libpki_config} --version`
 pkiversion_num=`echo $pkiversion | sed "s|\.||g"`
 
diff -ur openca-ocspd-3.1.2-orig/docs/Makefile.am openca-ocspd-3.1.2/docs/Makefile.am
--- openca-ocspd-3.1.2-orig/docs/Makefile.am	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/docs/Makefile.am	2023-07-14 16:39:36.396164742 +0200
@@ -34,17 +34,15 @@
 	$(POD2HTML) $< >$@
 
 install-man::
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)/share
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)/share/man
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)/share/man/man$(MANLEV)
-	@echo "Installing Man pages is $(DESTDIR)$(exec_prefix)/share/man/man$(MANLEV) ... "
+	@$(mkinstalldirs) $(DESTDIR)$(mandir)
+	@$(mkinstalldirs) $(DESTDIR)$(mandir)/man$(MANLEV)
+	@echo "Installing Man pages is $(DESTDIR)$(mandir)/man$(MANLEV) ... "
 	@for file in $(man_MANS) ; do \
        if test -f $$file ; then \
-        $(INSTALL_DATA) $$file $(DESTDIR)$(exec_prefix)/share/man/man$(MANLEV) ; \
+        $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$(MANLEV) ; \
        fi ; \
     done
-	@echo "Done. Add $(DESTDIR)$(exec_prefix)/share/man to MANPATH."
+	@echo "Done. Add $(DESTDIR)$(mandir) to MANPATH."
 
 # install-data-am: install-man
 # 	@$(NORMAL_INSTALL) ; \
diff -ur openca-ocspd-3.1.2-orig/docs/Makefile.in openca-ocspd-3.1.2/docs/Makefile.in
--- openca-ocspd-3.1.2-orig/docs/Makefile.in	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/docs/Makefile.in	2023-07-14 16:39:36.396164742 +0200
@@ -564,17 +564,15 @@
 	$(POD2HTML) $< >$@
 
 install-man::
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)/share
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)/share/man
-	@$(mkinstalldirs) $(DESTDIR)$(exec_prefix)/share/man/man$(MANLEV)
-	@echo "Installing Man pages is $(DESTDIR)$(exec_prefix)/share/man/man$(MANLEV) ... "
+	@$(mkinstalldirs) $(DESTDIR)$(mandir)
+	@$(mkinstalldirs) $(DESTDIR)$(mandir)/man$(MANLEV)
+	@echo "Installing Man pages is $(DESTDIR)$(mandir)/man$(MANLEV) ... "
 	@for file in $(man_MANS) ; do \
        if test -f $$file ; then \
-        $(INSTALL_DATA) $$file $(DESTDIR)$(exec_prefix)/share/man/man$(MANLEV) ; \
+        $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$(MANLEV) ; \
        fi ; \
     done
-	@echo "Done. Add $(DESTDIR)$(exec_prefix)/share/man to MANPATH."
+	@echo "Done. Add $(DESTDIR)$(mandir) to MANPATH."
 
 # install-data-am: install-man
 # 	@$(NORMAL_INSTALL) ; \
diff -ur openca-ocspd-3.1.2-orig/docs/ocspd.3 openca-ocspd-3.1.2/docs/ocspd.3
--- openca-ocspd-3.1.2-orig/docs/ocspd.3	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/docs/ocspd.3	2025-09-17 18:05:13.510293530 +0200
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.20)
+.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -38,6 +38,8 @@
 .    ds PI \(*p
 .    ds L" ``
 .    ds R" ''
+.    ds C`
+.    ds C'
 'br\}
 .\"
 .\" Escape single quotes in literal strings from groff's Unicode transform.
@@ -48,17 +50,24 @@
 .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
-.ie \nF \{\
-.    de IX
-.    tm Index:\\$1\t\\n%\t"\\$2"
+.\"
+.\" Avoid warning from groff about undefined register 'F'.
+.de IX
 ..
-.    nr % 0
-.    rr F
-.\}
-.el \{\
-.    de IX
+.nr rF 0
+.if \n(.g .if rF .nr rF 1
+.if (\n(rF:(\n(.g==0)) \{
+.    if \nF \{
+.        de IX
+.        tm Index:\\$1\t\\n%\t"\\$2"
 ..
+.        if !\nF==2 \{
+.            nr % 0
+.            nr F 2
+.        \}
+.    \}
 .\}
+.rr rF
 .\"
 .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
 .\" Fear.  Run.  Save yourself.  No user-serviceable parts.
@@ -124,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "ocspd.3 3"
-.TH ocspd.3 3 "2013-08-03" "openca-ocspd 3.1.0" "OpenCA Contributed Manual"
+.TH ocspd.3 3 "2022-06-27" "openca-ocspd 3.1.2" "OpenCA Contributed Manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -177,7 +186,7 @@
 .IP "\fB\-i passin\fR" 4
 .IX Item "-i passin"
 the key password source. For more information about the format of \fBarg\fR
-see the \fB\s-1PASS\s0 \s-1PHRASE\s0 \s-1ARGUMENTS\s0\fR section in \fIopenssl\fR\|(1).
+see the \fB\s-1PASS PHRASE ARGUMENTS\s0\fR section in \fIopenssl\fR\|(1).
 .IP "\fB\-engine id\fR" 4
 .IX Item "-engine id"
 specifying an engine (by it's unique \fBid\fR string) will cause the responder
diff -ur openca-ocspd-3.1.2-orig/docs/ocspd.3.pod openca-ocspd-3.1.2/docs/ocspd.3.pod
--- openca-ocspd-3.1.2-orig/docs/ocspd.3.pod	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/docs/ocspd.3.pod	2023-07-14 16:39:36.396164742 +0200
@@ -112,6 +112,8 @@
 
 L<openca(3)>,L<openssl(1)>, L<ocsp(1)>
 
+=back
+
 =cut
 
 
diff -ur openca-ocspd-3.1.2-orig/docs/ocspd.conf.3 openca-ocspd-3.1.2/docs/ocspd.conf.3
--- openca-ocspd-3.1.2-orig/docs/ocspd.conf.3	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/docs/ocspd.conf.3	2025-09-17 18:05:16.754293360 +0200
@@ -1,7 +1,15 @@
-.\" Automatically generated by Pod::Man 2.25 (Pod::Simple 3.20)
+.\" Automatically generated by Pod::Man 2.16 (Pod::Simple 3.05)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
+.de Sh \" Subsection heading
+.br
+.if t .Sp
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
 .de Sp \" Vertical space (when we can't use .PP)
 .if t .sp .5v
 .if n .sp
@@ -45,7 +53,7 @@
 .el       .ds Aq '
 .\"
 .\" If the F register is turned on, we'll generate index entries on stderr for
-.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
+.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index
 .\" entries marked with X<> in POD.  Of course, you'll have to process the
 .\" output yourself in some meaningful fashion.
 .ie \nF \{\
@@ -124,7 +132,7 @@
 .\" ========================================================================
 .\"
 .IX Title "ocspd.conf.3 3"
-.TH ocspd.conf.3 3 "2013-08-03" "openca-ocspd 3.1.0" "OpenCA Contributed Manual"
+.TH ocspd.conf.3 3 "2015-03-26" "openca-ocspd 3.1.2" "OpenCA Contributed Manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
diff -ur openca-ocspd-3.1.2-orig/etc/Makefile.am openca-ocspd-3.1.2/etc/Makefile.am
--- openca-ocspd-3.1.2-orig/etc/Makefile.am	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/etc/Makefile.am	2023-07-14 16:39:36.396164742 +0200
@@ -15,15 +15,15 @@
 	@$(NORMAL_INSTALL)
 	$(mkinstalldirs) $(etc_prefix); \
 	$(mkinstalldirs) $(etc_prefix)/init.d; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/certs; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/crls; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/private; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/ca.d \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki/token.d; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki/hsm.d; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki/profile.d
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/certs; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/crls; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/private; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/ca.d; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/pki; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/pki/token.d; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/pki/hsm.d; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/pki/profile.d
 	@for file in *.xml ; do \
 	    if test -f $$file; then \
 	      $(INSTALL_DATA) $$file $(etc_prefix)/ocspd; \
diff -ur openca-ocspd-3.1.2-orig/etc/Makefile.in openca-ocspd-3.1.2/etc/Makefile.in
--- openca-ocspd-3.1.2-orig/etc/Makefile.in	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/etc/Makefile.in	2023-07-14 16:39:36.396164742 +0200
@@ -473,15 +473,15 @@
 	@$(NORMAL_INSTALL)
 	$(mkinstalldirs) $(etc_prefix); \
 	$(mkinstalldirs) $(etc_prefix)/init.d; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/certs; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/crls; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/private; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/ca.d \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki/token.d; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki/hsm.d; \
-	$(mkinstalldirs) $(etc_prefix)/ocspd/pki/profile.d
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/certs; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/crls; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/private; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/ca.d; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/pki; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/pki/token.d; \
+	$(mkinstalldirs) -m 700 $(etc_prefix)/ocspd/pki/hsm.d; \
+	$(mkinstalldirs) -m 755 $(etc_prefix)/ocspd/pki/profile.d
 	@for file in *.xml ; do \
 	    if test -f $$file; then \
 	      $(INSTALL_DATA) $$file $(etc_prefix)/ocspd; \
diff -ur openca-ocspd-3.1.2-orig/etc/ocspd.xml.in openca-ocspd-3.1.2/etc/ocspd.xml.in
--- openca-ocspd-3.1.2-orig/etc/ocspd.xml.in	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/etc/ocspd.xml.in	2025-10-07 17:43:57.423369253 +0200
@@ -3,6 +3,10 @@
 <pki:serverConfig xmlns:pki="http://www.openca.org/openca/pki/1/0/0">
    <!-- General Section: details about server configuration and dirs -->
    <pki:general>
+      <!-- OCSP responder name - used for statistic logging (e.g. if more than
+           one OCSP responder is configured and all responder log into the same
+           database) -->
+      <!--<pki:responderName>OCSPD Test#1</pki:responderName>-->
       <!-- Directory where configurations about libPKI token (e.g., token.d/,
            hsm.d/, etc... ) are located -->
       <pki:pkiConfigDir>@prefix@/etc/ocspd/pki</pki:pkiConfigDir>
@@ -23,6 +27,48 @@
       <pki:crlReloadExpired>yes</pki:crlReloadExpired>
       <!-- Check CRLs validity every x seconds -->
       <pki:crlCheckValidity>600</pki:crlCheckValidity>
+      <!-- Skip CRL reload if it was not modified after the last load -->
+      <pki:crlCheckModificationTime>no</pki:crlCheckModificationTime>
+      <!-- Statistic logging
+        Entries can be given in form of <Identifier>[=ColumnName][,<Identifier>[=ColumnName]], etc.
+        The folowing identifier are supported:
+        Identifier     Default ColumnName
+        =========================================
+        ResponderName  responder_name
+        StartTime      starttime
+        EndTime        endtime
+        ArrivalTime    arrival_time
+        DepartureTime  departure_time
+        ResponseStatus response_status
+        CertStatus     cert_status
+        Serial         serialnumber
+        Issuer         issuer
+        CaName         caname
+        IP             ip
+        Duration       duration
+        CertType       cert_type
+        SKI            ski
+
+        If no ColumName is given the default one will be used.
+      -->
+
+      <!-- Specify the DB URI where the statistic info is written to -->
+      <!-- mysql://[<user>:<password>]@<host>[:<port>]/<dbname>/<table> -->
+      <!-- instead of specifying username/password here, the my.cnf file can be used -->
+      <!--<pki:logStatsUrl>mysql://dbuser:password@localhost/ocsp/statistics</pki:logStatsUrl>-->
+      <!--<pki:dbTimeout>5</pki:dbTimeout>-->
+      <!-- Specify the information (identifier) that shall be logged -->
+      <!--<pki:logStatsItems>ResponderName,StartTime,EndTime,ResponseStatus,CertStatus,Serial,CaName,Issuer,IP,Duration,CertType,SKI</pki:logStatsItems>-->
+      <!-- Ignore statistics logging if a request comes from the given IP (e.g. to ignore loadbalancer checks). Only one IP can be given. -->
+      <!--<pki:logStatsIgnoreIP>127.0.0.1</pki:logStatsIgnoreIP>-->
+
+      <!-- Log issuers CN (if y), otherwise log issuers whole DN (this is the default) -->
+      <!--<pki:logStatsCN>n</pki:logStatsCN>-->
+      <!-- Ignore statistics logging for failed connections (e.g. loadbalancer health checks) -->
+      <!--<pki:logStatsIgnoreFailedConnections>n</pki:logStatsIgnoreFailedConnections>-->
+      <!-- How long in seconds to cache responses. Zero means no caching. -->
+      <!--<pki:cacheDuration>30</pki:cacheDuration>-->
+      <!--<pki:logTiming>y</pki:logTiming>-->
    </pki:general>
    <!-- Security Related Configurations -->
    <pki:security>
@@ -40,6 +86,10 @@
       <!-- Address the server should bind to when starting, 0.0.0.0 binds
            to any available addresses, the default port is 2560 -->
       <pki:bindAddress>http://0.0.0.0:2560</pki:bindAddress>
+      <!-- Address the server should bind a health check port.
+           This is used to indicate any front end loadbalancer that the service will be not available.
+           Any incoming request on this port will be handled like a normal OCSP-Request. -->
+      <!--<pki:healthCheckAddress>http://0.0.0.0:2561</pki:healthCheckAddress>-->
       <!-- Use this to specify support for 1.0 or 1.1 HTTP -->
       <pki:httpProtocol>1.0</pki:httpProtocol>
       <!-- Use httpBaseURL if you want to respond only to certain URLs,
@@ -48,17 +98,41 @@
       <!-- Timeout used for incoming connections, the server will close the
            socket if no data is received within `timeout` seconds -->
       <pki:timeOut>5</pki:timeOut>
+      <!-- HTTP Persistent connection - if 'no' (default) the ocspd closes the connection after sending a response.
+           If 'yes' the ocspd waits for next data (or the client closes the connection).
+           If no more data is sent, the connection will be closed by the ocspd after timeOut seconds. -->
+      <!--<pki:httpPersistent>y</pki:httpPersistent>-->
    </pki:network>
-   <!-- OCSP response configuration -->
-   <pki:response>
+   <!-- OCSP request configuration -->
+   <pki:request>
+      <!-- Maximum length of nonce value in bytes -->
+      <pki:maximumNonceLength>32</pki:maximumNonceLength>
       <!-- Max size of incoming requests -->
       <pki:maxReqSize>8192</pki:maxReqSize>
+      <!-- Maximum serial number of incoming requests (exponent to the power of two). The
+           maximum serial number allowed in a request is then calculated with ((2^n)-1).
+           If configured it is also checked if the requested serial is negative (which is not allowed) -->
+      <pki:maxSerial>160</pki:maxSerial>
+      <!-- Maximum number of requests contained in a single OCSP request -->
+      <pki:maxRequests>1</pki:maxRequests>
+   </pki:request>
+   <!-- OCSP response configuration -->
+   <pki:response>
+      <!-- Digest Algorithm used for building the issuerNameHash and
+           issuerKeyHash digest. More than one value can be used:
+           e.g. >SHA1,SHA256<
+           The algorithms must be given in capital letters. Every algorithm
+           supported by OpenSSL can be used.
+           If not configured the digestAlgorithm (see below) is used. -->
+      <pki:issuerHashDigestAlgorithm>SHA1,SHA256,SHA384,SHA512</pki:issuerHashDigestAlgorithm>
       <!-- Digest Algorithm to be used when building responses, currently
            the standard specifies SHA1 as the only supported algorithm -->
       <pki:digestAlgorithm>SHA1</pki:digestAlgorithm>
       <!-- Digest Algorithm to be used when signing responses, currently
            for some CISCO devices SHA1 is the only supported algorithm -->
       <pki:signatureDigestAlgorithm>SHA1</pki:signatureDigestAlgorithm>
+      <!-- Don't include an optional reasonCode (contained in a CRL) in the response -->
+      <pki:ignoreReasonCode>no</pki:ignoreReasonCode>
       <!-- Validity Period of responses, clients are not supposed to ask
            informations about the same CA within this validity period
            If the two options are both set to '0' the 'nextUpdate' field
diff -ur openca-ocspd-3.1.2-orig/etc/token.d/eracom.xml.in openca-ocspd-3.1.2/etc/token.d/eracom.xml.in
--- openca-ocspd-3.1.2-orig/etc/token.d/eracom.xml.in	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/etc/token.d/eracom.xml.in	2023-07-14 16:39:36.396164742 +0200
@@ -20,5 +20,5 @@
   <pki:password>1234567890</pki:password>
   <!-- <pki:password></pki:password> -->
   <!-- Certificates -->
-  <pki:othercerts>file:://$USER/.pki/certs.pem</pki:othercerts>
+  <pki:otherCerts>file:://$USER/.pki/certs.pem</pki:otherCerts>
 </pki:tokenConfig>
diff -ur openca-ocspd-3.1.2-orig/etc/token.d/etoken.xml.in openca-ocspd-3.1.2/etc/token.d/etoken.xml.in
--- openca-ocspd-3.1.2-orig/etc/token.d/etoken.xml.in	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/etc/token.d/etoken.xml.in	2023-07-14 16:39:36.396164742 +0200
@@ -21,5 +21,5 @@
   <pki:password>1234567890</pki:password>
   <!-- <pki:password>mil4yer</pki:password> -->
   <!-- Certificates -->
-  <pki:othercerts>file://$HOME/.libpki/certs.pem</pki:othercerts>
+  <pki:otherCerts>file://$HOME/.libpki/certs.pem</pki:otherCerts>
 </pki:tokenConfig>
diff -ur openca-ocspd-3.1.2-orig/etc/token.d/software.xml.in openca-ocspd-3.1.2/etc/token.d/software.xml.in
--- openca-ocspd-3.1.2-orig/etc/token.d/software.xml.in	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/etc/token.d/software.xml.in	2023-07-14 16:39:36.396164742 +0200
@@ -14,8 +14,8 @@
   <!-- CA Certificate -->
   <pki:cacert>file://@prefix@/etc/ocspd/certs/cacert.pem</pki:cacert>
   <!-- Certificates -->
-  <pki:othercerts>file:://@prefix@/etc/ocspd/certs/other-certs.pem</pki:othercerts>
-  <pki:trustedcerts>file:://@prefix@/etc/ocspd/certs/trusted-certs.pem</pki:trustedcerts>
+  <!--<pki:otherCerts>file:://@prefix@/etc/ocspd/certs/other-certs.pem</pki:otherCerts>-->
+  <!--<pki:trustedCerts>file:://@prefix@/etc/ocspd/certs/trusted-certs.pem</pki:trustedCerts>-->
   <!-- passin is used to specify the method for reading the token
        password. The following options are available:
          none ...... : do not prompt for any password
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/config.c openca-ocspd-3.1.2/src/ocspd/config.c
--- openca-ocspd-3.1.2-orig/src/ocspd/config.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/config.c	2025-11-07 15:17:17.099865042 +0100
@@ -14,11 +14,257 @@
  */
 
 #include "general.h"
+#include <libpki/net/pki_mysql.h>
+#include <sys/stat.h>
+#include <libmemcached-1.0/memcached.h>
 
+
+/* Internal data structure */
+typedef struct {
+	const char *name;
+	unsigned long flag;
+	unsigned long mask;
+} NAME_EX_TBL;
+
+ 
 /* External imported variables */
 extern OCSPD_CONFIG * ocspd_conf;
 
 /* Functions */
+static int OCSPD_EVP_MD_STACK_add_md(STACK_OF(EVP_MD) **mds, const EVP_MD *md)
+{
+	if (!mds)
+		return 0;
+
+	/* Create new md stack if necessary. */
+	if (!*mds && !(*mds = sk_EVP_MD_new_null()))
+		return 0;
+
+	/* Add the shared md, no copy needed. */
+	if (!sk_EVP_MD_push(*mds, (EVP_MD *)md))
+		return 0;
+
+	return 1;
+}
+
+static char *bin_to_string(const unsigned char *buffer, size_t len)
+{
+    char *tmp, *q;
+    const unsigned char *p;
+    int i;
+    const static char hexdig[] = "0123456789ABCDEF";
+
+    if (!buffer || !len)
+        return NULL;
+    if (!(tmp = PKI_Malloc(len * 2 + 1))) {
+        PKI_log_err("Memory allocation error!");
+        return NULL;
+    }
+    q = tmp;
+    for (i = 0, p = buffer; i < len; i++, p++) {
+        *q++ = hexdig[(*p >> 4) & 0xf];
+        *q++ = hexdig[*p & 0xf];
+    }
+    *q = 0;
+
+    return tmp;
+}
+
+static int set_table_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl)
+{
+  char c;
+  const NAME_EX_TBL *ptbl;
+  c = arg[0];
+
+  if(c == '-') {
+    c = 0;
+    arg++;
+  } else if (c == '+') {
+    c = 1;
+    arg++;
+  } else c = 1;
+
+  for(ptbl = in_tbl; ptbl->name; ptbl++) {
+    if(!strcasecmp(arg, ptbl->name)) {
+      *flags &= ~ptbl->mask;
+      if(c) *flags |= ptbl->flag;
+      else *flags &= ~ptbl->flag;
+      return 1;
+    }
+  }
+  return 0;
+}
+
+static int set_multi_opts(unsigned long *flags, const char *arg, const NAME_EX_TBL *in_tbl)
+{
+  STACK_OF(CONF_VALUE) *vals;
+  CONF_VALUE *val;
+  int i, ret = 1;
+  if(!arg) return 0;
+  vals = X509V3_parse_list(arg);
+  for (i = 0; i < sk_CONF_VALUE_num(vals); i++) {
+    val = sk_CONF_VALUE_value(vals, i);
+    if (!set_table_opts(flags, val->name, in_tbl))
+      ret = 0;
+  }
+  sk_CONF_VALUE_pop_free(vals, X509V3_conf_free);
+  return ret;
+}
+
+static int set_name_ex(unsigned long *flags, const char *arg)
+{ 
+  static const NAME_EX_TBL ex_tbl[] = {
+    { "esc_2253", ASN1_STRFLGS_ESC_2253, 0},
+    { "esc_ctrl", ASN1_STRFLGS_ESC_CTRL, 0},
+    { "esc_msb", ASN1_STRFLGS_ESC_MSB, 0},
+    { "use_quote", ASN1_STRFLGS_ESC_QUOTE, 0},
+    { "utf8", ASN1_STRFLGS_UTF8_CONVERT, 0},
+    { "ignore_type", ASN1_STRFLGS_IGNORE_TYPE, 0},
+    { "show_type", ASN1_STRFLGS_SHOW_TYPE, 0},
+    { "dump_all", ASN1_STRFLGS_DUMP_ALL, 0},
+    { "dump_nostr", ASN1_STRFLGS_DUMP_UNKNOWN, 0},
+    { "dump_der", ASN1_STRFLGS_DUMP_DER, 0},
+    { "compat", XN_FLAG_COMPAT, 0xffffffffL},
+    { "sep_comma_plus", XN_FLAG_SEP_COMMA_PLUS, XN_FLAG_SEP_MASK},
+    { "sep_comma_plus_space", XN_FLAG_SEP_CPLUS_SPC, XN_FLAG_SEP_MASK},
+    { "sep_semi_plus_space", XN_FLAG_SEP_SPLUS_SPC, XN_FLAG_SEP_MASK},
+    { "sep_multiline", XN_FLAG_SEP_MULTILINE, XN_FLAG_SEP_MASK},
+    { "dn_rev", XN_FLAG_DN_REV, 0},
+    { "nofname", XN_FLAG_FN_NONE, XN_FLAG_FN_MASK},
+    { "sname", XN_FLAG_FN_SN, XN_FLAG_FN_MASK},
+    { "lname", XN_FLAG_FN_LN, XN_FLAG_FN_MASK},
+    { "align", XN_FLAG_FN_ALIGN, 0},
+    { "oid", XN_FLAG_FN_OID, XN_FLAG_FN_MASK},
+    { "space_eq", XN_FLAG_SPC_EQ, 0},
+    { "dump_unknown", XN_FLAG_DUMP_UNKNOWN_FIELDS, 0},
+    { "RFC2253", XN_FLAG_RFC2253, 0xffffffffL},
+    { "oneline", XN_FLAG_ONELINE, 0xffffffffL},
+    { "multiline", XN_FLAG_MULTILINE, 0xffffffffL},
+    { "ca_default", XN_FLAG_MULTILINE, 0xffffffffL},
+    { NULL, 0, 0}
+  };
+  return set_multi_opts(flags, arg, ex_tbl);
+}
+
+static int parse_stats_items(char *items, unsigned long *item_flags, char *base_url, URL **log_stats_url)
+{
+	char *column;
+	char *next;
+	LOG_STATS_ITEM_TBL *tbl;
+	LOG_STATS_ITEM_TBL *tbl_cpy = NULL;
+	BIO   *membio = NULL;
+	int   ret = 1;
+	long  len = 0;
+
+
+	// copy original ocsp_stats_item_tbl due to static const definition
+	// we need this to modify the entry element for ordering the occurence of the logStatsItems
+	if( (tbl_cpy = PKI_Malloc(sizeof(ocsp_stats_item_tbl))) == NULL)
+	{
+		PKI_log_err("Memory allocation error!");
+		return(ret);
+	}
+	memcpy(tbl_cpy, ocsp_stats_item_tbl, sizeof(ocsp_stats_item_tbl));
+
+
+	if(base_url)
+	{
+		if( (membio = BIO_new(BIO_s_mem())) == NULL)
+		{
+			PKI_log_err("Memory allocation error!");
+			goto end;
+		}
+
+		if(BIO_printf(membio, "%s?", base_url) <= 0)
+		{
+			PKI_log_err ("BIO_printf(base_url) failed!\n");
+			goto end;
+		}
+	}
+
+	*item_flags = 0;
+
+	while(*items)
+	{
+		if( (next = strchr(items, ',')) != NULL)
+			*next++ = 0;
+
+		if( (column = strchr(items, '=')) != NULL)
+		{
+			*column++ = 0;
+			if(*column == '\0')
+				column = NULL;
+		}
+
+		for(tbl = tbl_cpy; tbl->name; tbl++)
+		{
+			if(strcmp_nocase(items, tbl->name) == 0)
+			{
+				if(base_url)
+				{
+					if(column)
+						tbl->entry = column;
+					else
+						tbl->entry = tbl->column;;
+				}
+
+				*item_flags |= tbl->flag;
+
+				break;
+			}
+		}
+		items = next;
+
+		if(items == NULL)
+			break;
+	}
+
+	if(base_url)
+	{
+		// temporary usage of len to indicate the number of items
+		for(tbl = tbl_cpy; tbl->name; tbl++)
+		{
+			if(*item_flags & tbl->flag)
+			{
+				if(BIO_printf(membio, "%s%s", len++ ? ",":"", tbl->entry) <= 0)
+				{
+					PKI_log_err ("BIO_printf(column) failed!\n");
+					goto end;
+				}
+			}
+		}
+
+		if(BIO_write(membio, "\x0", 1) <= 0)
+		{
+			PKI_log_err ("BIO_write() failed!\n");
+			goto end;
+		}
+
+		if( (len = BIO_get_mem_data(membio, &column) ) <= 0)
+		{
+			PKI_log_err ( "BIO_get_mem_data() failed");
+			goto end;
+		}
+
+		if ((*log_stats_url = URL_new ( column )) == NULL)
+		{
+			PKI_log_err ( "URL not parsable (%s)", column );
+			goto end;
+		}
+
+		PKI_log_debug("Created dbUrl: %s", column);
+	}
+
+	ret = 0;
+
+end:
+	if(membio) BIO_free(membio);
+	if(tbl_cpy) PKI_Free(tbl_cpy);
+
+	return(ret);
+}
+
+
 OCSPD_CONFIG * OCSPD_load_config(char *configfile)
 {
 	OCSPD_CONFIG *h = NULL;
@@ -61,13 +307,21 @@
 	h->verbose   = 0;
 	h->debug     = 0;
 	h->nthreads  = 5;
-	h->http_proto = "1.0";
+	h->http_proto = strdup("1.0");
 	h->max_timeout_secs = 5;
+	h->listenfd_hc = -1;
 
 	h->crl_auto_reload = 3600;
 	h->crl_reload_expired = 1;
 	h->crl_check_validity = 600;
 
+	h->log_stats_ignore_failed_connections = 1;
+
+	/* Default settings as previously hardcoded */
+	h->cache_settings = strdup("--SERVER=127.0.0.1 --BINARY-PROTOCOL");
+	h->cache_duration = 0;
+	h->log_timing = 0;
+
 	/* Copy the config filename so that it could be re-loaded on SIGHUP */
 	h->cnf_filename = strdup( configfile );
 
@@ -82,36 +336,37 @@
 		PKI_COND_init ( &h->condVars[i] );
 	}
 
-	PKI_RWLOCK_init ( &h->crl_lock );
+	/* responderName */
+	if (( tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/responderName")) != NULL)
+	{
+		h->responder_name = tmp_s;
+	}
 
 	/* Token Initialization */
 	if (( tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/pkiConfigDir")) == NULL)
 	{
 		PKI_log_err("Missing pkiConfigDir in configuration!");
-		return NULL;
+		goto err;
 	}
 	else 
 	{
+		h->token_config_dir = strdup ( tmp_s );
+
 		if ((tmp_s2 = PKI_CONFIG_get_value( cnf, "/serverConfig/general/token" )) != NULL)
 		{
 			h->token_name = strdup( tmp_s2 );
-			h->token_config_dir = strdup ( tmp_s );
 
 			if ((h->token = PKI_TOKEN_new_null()) == NULL)
 			{
 				PKI_log( PKI_LOG_ERR, "Memory error for new token");
-				exit(1);
+				PKI_Free(tmp_s);
+				goto err;
 			}
 
 			PKI_Free(tmp_s2);
 		}
 		else
-		{
-			PKI_log_err("No General Token provided in configuration.");
-
-			PKI_Free(tmp_s);
-			return NULL;
-		}
+			PKI_log(PKI_LOG_INFO, "No General Token provided in configuration.");
 
 		PKI_Free(tmp_s);
 	}
@@ -183,7 +438,7 @@
 		PKI_Free(tmp_s);
 	}
 
-	/* AutoReload timeout */
+	/* ReloadExpired */
 	if ((tmp_s = PKI_CONFIG_get_value( cnf, 
 				"/serverConfig/general/crlReloadExpired")) != NULL )
 	{
@@ -196,6 +451,144 @@
 		PKI_Free(tmp_s);
 	}
 
+	/* CheckModificationTime */
+	if((tmp_s = PKI_CONFIG_get_value( cnf, 
+				"/serverConfig/general/crlCheckModificationTime")) != NULL ) {
+
+		if (strncmp_nocase(tmp_s, "y", 1) == 0)
+		{
+			h->crl_check_mtime = 1;
+			PKI_log(PKI_LOG_INFO, "CRL check of modification time enabled");
+		}
+		else
+			PKI_log(PKI_LOG_INFO, "CRL check of modification time disabled");
+		PKI_Free(tmp_s);
+	}
+
+	/* logTiming */
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/logTiming")) != NULL)
+	{
+		if (strncmp_nocase ( "y", tmp_s, 1 ) == 0 )
+		{
+			h->log_timing = 1;
+			PKI_log(PKI_LOG_INFO, "logTiming: Log additional timing information");
+		}
+
+		PKI_Free(tmp_s);
+	}
+
+	/* logStatsUrl */
+	tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/logStatsUrl");
+
+
+	/* logStatsItems */
+	if ((tmp_s2 = PKI_CONFIG_get_value( cnf, "/serverConfig/general/logStatsItems")) != NULL)
+	{
+		if(parse_stats_items(tmp_s2, &(h->log_stats_flags), tmp_s, &(h->log_stats_url)) != 0)
+			PKI_log_err ( "logStatsItems not parsable. No statistics logging will be performed." );
+
+		PKI_Free(tmp_s2);
+	}
+	if(tmp_s) PKI_Free(tmp_s);
+
+	/* logStatsCN */
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, 
+				"/serverConfig/general/logStatsCN")) != NULL )
+	{
+		if (strncmp_nocase(tmp_s, "y", 1) == 0)
+		{
+			h->log_stats_dn = 1;
+			PKI_log(PKI_LOG_INFO, "logStatsCN: Log issuers CN");
+		}
+
+		PKI_Free(tmp_s);
+	}
+
+	/* logStatsIgnoreFailedConnections */
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, 
+				"/serverConfig/general/logStatsIgnoreFailedConnections")) != NULL )
+	{
+		if (strncmp_nocase(tmp_s, "y", 1) == 0)
+		{
+			h->log_stats_ignore_failed_connections = 1;
+			PKI_log(PKI_LOG_INFO, "logStats: Ignore failed connections");
+		}
+		else
+		{
+			h->log_stats_ignore_failed_connections = 0;
+			PKI_log(PKI_LOG_INFO, "logStats: Log also failed connections");
+		}
+
+		PKI_Free(tmp_s);
+	}
+
+	/* logStatsIgnoreIP */
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/logStatsIgnoreIP")) != NULL)
+	{
+		char *items = tmp_s;
+		char *next;
+		size_t num = 0;
+		struct in_addr *iaddr;
+
+		// first count the number of available values
+		while(items)
+		{
+			if( (next = strchr(items, ',')) != NULL)
+				next++;
+
+			num++;
+			items = next;
+		}
+
+		if((h->ignore_ip_list = (IGNORE_IP_LIST *) 
+				PKI_Malloc ( sizeof (IGNORE_IP_LIST))) == NULL) {
+			PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		// allocate base array
+		if( (h->ignore_ip_list->value = PKI_Malloc ( num * sizeof (struct in_addr *))) == NULL) {
+			PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		// then copy each value to our array
+		h->ignore_ip_list->nval = num;
+		num = 0;
+		items = tmp_s;
+		while(items)
+		{
+			if( (next = strchr(items, ',')) != NULL)
+				*next++ = 0;
+
+			// skip possible spaces
+			while(*items == ' ' && *items != 0)
+				items++;
+
+			PKI_log( PKI_LOG_INFO, "Item: '%s'", items);
+			if( (iaddr = PKI_Malloc (sizeof (struct in_addr))) == NULL) {
+				PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+				PKI_Free(tmp_s);
+				goto err;
+			}
+
+			if(inet_aton((const char *)items, iaddr) == 0)
+			{
+				PKI_log_err ("Cannot convert logStatsIgnoreIP %s: %d (%s)", items, errno, strerror(errno) );
+				goto err;
+			}
+      else
+				h->ignore_ip_list->value[num++] = iaddr;
+
+			items = next;
+		}
+		
+		PKI_Free(tmp_s);
+	}
+
+
 	/* Server Privileges */
 	if ((tmp_s = PKI_CONFIG_get_value(cnf, "/serverConfig/security/user")) != NULL)
 	{
@@ -215,6 +608,20 @@
 		PKI_Free(tmp_s);
 	}
 
+	/* Health Check Address */
+	if((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/network/healthCheckAddress" )) != NULL)
+	{
+		if ((h->healthCheckUrl = URL_new( tmp_s )) == NULL)
+		{
+			PKI_log( PKI_LOG_ERR, "Can't parse bindAddress (%s)", tmp_s );
+			PKI_Free(tmp_s);
+
+			goto err;
+		}
+
+		PKI_Free(tmp_s);
+	}
+
 	/* Bind Address */
 	if((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/network/bindAddress" )) == NULL)
 	{
@@ -236,6 +643,7 @@
 	/* HTTP Version */
 	if((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/network/httpProtocol")) != NULL)
 	{
+		if(h->http_proto) PKI_Free(h->http_proto);
 		h->http_proto = strdup(tmp_s);
 		PKI_Free(tmp_s);
 	}
@@ -249,9 +657,37 @@
 		PKI_Free(tmp_s);
 	}
 
+	/* HTTP Persistent connections */
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/network/httpPersistent")) != NULL)
+	{
+		if (strncmp_nocase ( "y", tmp_s, 1 ) == 0 )
+		{
+			h->http_persistent = 1;
+		}
+
+		PKI_Free(tmp_s);
+	}
+
+	/* maximumNonceLength */
+	if((tmp_s = PKI_CONFIG_get_value( cnf, 
+				"/serverConfig/request/maximumNonceLength")) != NULL ) {
+		int t = 0;
+
+		if((t = atoi( tmp_s )) > 0 ) {
+			h->max_nonce_length = t;
+		}
+		PKI_Free(tmp_s);
+	}
+
 	/* Maximum Request Size */
 	if((tmp_s = PKI_CONFIG_get_value( cnf,
-				"/serverConfig/response/maxReqSize" )) != NULL ) {
+				"/serverConfig/request/maxReqSize" )) == NULL ) {
+
+		/* Fallback for old configuration files */
+		tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/response/maxReqSize" );
+	}
+
+	if(tmp_s) {
 		int t = 0;
 
 		if((t = atoi( tmp_s )) > 0 ) {
@@ -260,11 +696,66 @@
 		PKI_Free(tmp_s);
 	}
 
+	/* maximum serial number */
+	if((tmp_s = PKI_CONFIG_get_value(cnf, "/serverConfig/request/maxSerial")) != NULL)
+	{
+		BIGNUM *bn = NULL;
+
+		if( (h->max_serial = BN_new() ) == NULL)
+		{
+			PKI_log_debug("BN_new failed");
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		if( (bn = BN_new() ) == NULL)
+		{
+			PKI_log_debug("BN_new failed");
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		// for the maximum supported serial number in requests we calculate: (2^n)-1
+		if(!BN_lshift(bn, BN_value_one(), atoi(tmp_s)))
+		{
+			PKI_log_debug("BN_lshift failed");
+			BN_free(bn);
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		if(!BN_sub(h->max_serial, bn, BN_value_one()))
+		{
+			PKI_log_debug("BN_sub failed");
+			BN_free(bn);
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		if( (tmp_s2 = BN_bn2hex(h->max_serial) ) != NULL)
+			PKI_log(PKI_LOG_INFO, "Maximum supported serial number is: 0x%s", tmp_s2);
+
+		PKI_Free(tmp_s);
+		BN_free(bn);
+
+		if(tmp_s2)
+			OPENSSL_free(tmp_s2);
+		else
+			goto err;
+	}
+
+	/* maximum number of requests contained in a single OCSP request */
+	if((tmp_s = PKI_CONFIG_get_value(cnf, "/serverConfig/request/maxRequests")) != NULL)
+	{
+		h->max_request_count = atoi(tmp_s);
+		PKI_Free(tmp_s);
+	}
+
 
 	// Default
 	h->digest = PKI_DIGEST_ALG_SHA1;
 
-	/* Digest Algorithm to be used */
+	/* Digest algorithm to be used */
 	if ((tmp_s = PKI_CONFIG_get_value(cnf, "/serverConfig/response/digestAlgorithm" )) != NULL)
 	{
 		h->digest = PKI_DIGEST_ALG_get_by_name( tmp_s );
@@ -272,7 +763,8 @@
 		if (!h->digest) 
 		{
 			PKI_log_err("Can not parse response digest algorithm: %s", tmp_s);
-			exit(1);
+			PKI_Free(tmp_s);
+			goto err;
 		}
 		else PKI_log_debug("Selected response digest algorithm: %s", tmp_s);
 
@@ -293,13 +785,87 @@
 		if (!h->sigDigest) 
 		{
 			PKI_log_err("Can not parse signing digest algorithm: %s", tmp_s);
-			exit(1);
+			PKI_Free(tmp_s);
+			goto err;
 		}
 		else PKI_log_debug("Selected signature digest algorithm: %s", tmp_s);
 
 		PKI_Free(tmp_s);
 	}
 
+	/* Digest algorithm used for building the issuerNameHash and issuerKeyHash */
+	if((tmp_s = PKI_CONFIG_get_value( cnf,
+			"/serverConfig/response/issuerHashDigestAlgorithm" )) != NULL )
+	{
+		EVP_MD *digest;
+		char *p1, *p2;
+
+		p1 = tmp_s;
+
+		while( (p2 = strchr(p1, ',') ) != NULL)
+		{
+			*p2++ = 0;
+
+			digest = PKI_DIGEST_ALG_get_by_name( p1 );
+			if (!digest) 
+			{
+				PKI_log_err("Can not parse issuerHashDigestAlgorithm: %s", p1);
+				goto err;
+			}
+
+			if(!OCSPD_EVP_MD_STACK_add_md(&(h->issuerHashDigest), digest))
+			{
+				PKI_log_err("Can not add issuerHashDigestAlgorithm");
+				goto err;
+			}
+
+			PKI_log_debug("Selected issuerHashDigestAlgorithm: %s", p1);
+
+			/* Skip possible spaces */
+			while(*p2 == ' ')
+        p2++;
+			p1 = p2;
+		}
+
+		digest = PKI_DIGEST_ALG_get_by_name( p1 );
+		if (!digest) 
+		{
+			PKI_log_err("Can not parse issuerHashDigestAlgorithm: %s", p1);
+			goto err;
+		}
+
+		if(!OCSPD_EVP_MD_STACK_add_md(&(h->issuerHashDigest), digest))
+		{
+			PKI_log_err("Can not add issuerHashDigestAlgorithm: %s", p1);
+			goto err;
+		}
+
+		PKI_log_debug("Selected issuerHashDigestAlgorithm: %s", p1);
+		PKI_Free(tmp_s);
+	}
+	else
+	{
+		/* for backward compatibility we use the configured digestAlgorithm */
+		if(!OCSPD_EVP_MD_STACK_add_md(&(h->issuerHashDigest), h->digest))
+		{
+			PKI_log_err("Can not add issuerHashDigestAlgorithm: %s", EVP_MD_name(PKI_DIGEST_ALG_DEFAULT));
+			goto err;
+		}
+
+		PKI_log_debug("Selected issuerHashDigestAlgorithm: %s", EVP_MD_name(PKI_DIGEST_ALG_DEFAULT));
+	}
+
+	/* ignoreReasonCode */
+	if ((tmp_s = PKI_CONFIG_get_value(cnf, "/serverConfig/response/ignoreReasonCode")) != NULL)
+	{
+		if (strncmp_nocase(tmp_s, "y", 1) == 0) 
+		{
+			h->ignore_reason_code = 1;
+		}
+
+		PKI_Free(tmp_s);
+	}
+
 	/* Now Parse the PRQP Response Section */
 	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/response/validity/days" )) != NULL)
 	{
@@ -315,6 +881,31 @@
 
 	h->set_nextUpdate = h->ndays * 3600 + h->nmin * 60;
 
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/cacheSettings" )) != NULL)
+	{
+		memcached_return_t retval;
+		char errbuf[2000];
+
+		memset(errbuf, 0, 2000);
+		retval = libmemcached_check_configuration(tmp_s, strlen(tmp_s), errbuf, sizeof errbuf);
+		if (retval != MEMCACHED_SUCCESS)
+		{
+			PKI_log_err("libmemcached configuration failure %d: %s", retval, errbuf);
+			PKI_Free(tmp_s);
+			goto err;
+		}
+
+		if (h->cache_settings) PKI_Free(h->cache_settings);
+		h->cache_settings = strdup(tmp_s);
+		PKI_Free(tmp_s);
+	}
+
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/cacheDuration" )) != NULL)
+	{
+		h->cache_duration = atoi(tmp_s);
+		PKI_Free(tmp_s);
+	}
+
 	/* Database Options */
 	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/dbUrl")) != NULL)
 	{
@@ -328,13 +919,26 @@
 		PKI_Free(tmp_s);
 	}
 
+	/* Database connection timeout in seconds*/
+	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/dbTimeout")) != NULL)
+	{
+		h->db_ctimeout = atoi(tmp_s);
+		PKI_Free(tmp_s);
+	}
+
 	/* Database Persistant */
 	if ((tmp_s = PKI_CONFIG_get_value( cnf, "/serverConfig/general/dbPersistant")) != NULL)
 	{
 		if (strncmp_nocase ( "n", tmp_s, 1 ) == 0 )
+		{
 			h->db_persistant = 0;
+		}
 		else 
+		{
 			h->db_persistant = 1;
+			if(h->db_url->proto == URI_PROTO_MYSQL)
+				(void)db_init(DB_MYSQL_PERSISTENT);
+		}
 
 		PKI_Free(tmp_s);
 	}
@@ -345,186 +949,772 @@
 	{
 
 		PKI_log(PKI_LOG_ERR, "Can not build CA list!");
-		if (ca_config_stack) PKI_STACK_CONFIG_free ( ca_config_stack );
 		goto err;
 	}
 
-	if (ca_config_stack) PKI_STACK_CONFIG_free ( ca_config_stack );
+	if (ca_config_stack) PKI_STACK_CONFIG_free_all ( ca_config_stack );
+	if( cnf ) PKI_CONFIG_free ( cnf );
 
 	return ( h );
 
-err:
-	if( ca_config_stack ) PKI_STACK_CONFIG_free ( ca_config_stack );
-	if( cnf ) PKI_CONFIG_free ( cnf );
-	if( h ) PKI_Free ( h );
+err:
+	if( ca_config_stack ) PKI_STACK_CONFIG_free_all ( ca_config_stack );
+	if( cnf ) PKI_CONFIG_free ( cnf );
+	if( h ) {
+		OCSPD_free_config(h);
+		PKI_Free ( h );
+	}
+
+	return( NULL );
+}
+
+void OCSPD_free_ca_list(PKI_STACK *ca_list) {
+
+	CA_LIST_ENTRY *ca = NULL;
+
+	if(!ca_list) return;
+
+	while ( (ca = PKI_STACK_pop ( ca_list ) ) ) {
+		CA_LIST_ENTRY_free(ca);
+	}
+
+	PKI_STACK_free_all(ca_list);
+}
+
+void OCSPD_free_config(OCSPD_CONFIG *cnf) {
+
+  int i;
+
+	if(!cnf) return;
+
+	if(cnf->cnf_filename)     PKI_Free(cnf->cnf_filename);
+	if(cnf->responder_name)   PKI_Free(cnf->responder_name);
+	if(cnf->token_name)       PKI_Free(cnf->token_name);
+	if(cnf->token_config_dir) PKI_Free(cnf->token_config_dir);
+	if(cnf->token)            PKI_TOKEN_free_void(cnf->token);
+	if(cnf->ca_config_dir)    PKI_Free(cnf->ca_config_dir);
+	if(cnf->pidfile)          PKI_Free(cnf->pidfile);
+	if(cnf->user)             PKI_Free(cnf->user);
+	if(cnf->group)            PKI_Free(cnf->group);
+	if(cnf->chroot_dir)       PKI_Free(cnf->chroot_dir);
+	if(cnf->bindUrl)          URL_free(cnf->bindUrl);
+	if(cnf->healthCheckUrl)   URL_free(cnf->healthCheckUrl);
+	if(cnf->http_proto)       PKI_Free(cnf->http_proto);
+	if(cnf->db_url)           URL_free(cnf->db_url);
+	if(cnf->log_stats_url)    URL_free(cnf->log_stats_url);
+	if(cnf->ca_list)          OCSPD_free_ca_list(cnf->ca_list);
+	if(cnf->threads_list)     PKI_Free(cnf->threads_list);
+	if(cnf->issuerHashDigest) sk_EVP_MD_free(cnf->issuerHashDigest);
+	if(cnf->max_serial)       BN_free(cnf->max_serial);
+	if(cnf->cache_settings)   PKI_Free(cnf->cache_settings);
+
+	for( i = 0; i < sizeof ( cnf->mutexes ) / sizeof( PKI_MUTEX ); i++ )
+	{
+		PKI_MUTEX_destroy ( &cnf->mutexes[i] );
+	}
+
+	if ( cnf->ignore_ip_list )
+	{
+		int i;
+
+		for(i = 0; i < cnf->ignore_ip_list->nval; i++)
+			PKI_Free (cnf->ignore_ip_list->value[i] );
+		PKI_Free ( cnf->ignore_ip_list->value );
+		PKI_Free ( cnf->ignore_ip_list );
+	}
+}
+ 
+char *OCSPD_get_cn(PKI_X509_CERT *cert, unsigned int cn_only)
+{
+	BIO     *bio  = NULL;
+	char    *buf  = NULL;
+	char    *ptr  = NULL;
+	size_t  len   = 0;
+	int     pos   = -1;
+	X509_NAME *name     = NULL;
+	X509_NAME_ENTRY *ne = NULL;
+	unsigned long flags = ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_UTF8_CONVERT | XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | XN_FLAG_FN_SN;
+
+
+	if( (bio = BIO_new(BIO_s_mem())) == NULL)
+	{
+		PKI_log_err ("BIO_new() Memory Allocation error");
+		return(NULL);
+	}
+
+	if(cn_only)
+	{
+		// Only CN is extracted
+
+		BIO_write(bio, "CN=", 3);
+
+		if( (name = X509_get_subject_name(cert->value) ) == NULL)
+		{
+			PKI_log_err ("X509_get_issuer_name() failed");
+			goto end;
+		}
+
+		if( (pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos) ) < 0)
+		{
+			PKI_log_err ("X509_NAME_get_index_by_NID(NID_commonName) failed");
+			goto end;
+		}
+
+		if( (ne = X509_NAME_get_entry(name, pos) ) == NULL)
+		{
+			PKI_log_err ("X509_NAME_get_entry(commonName) failed");
+			goto end;
+		}
+
+		if(ASN1_STRING_print_ex(bio, X509_NAME_ENTRY_get_data(ne), flags) < 0)
+		{
+			PKI_log_err ("ASN1_STRING_print_ex(commonName) failed");
+			goto end;
+		}
+	}
+	else
+	{
+		// Alternatively we can get the whole subject
+		X509_NAME_print_ex(bio, PKI_X509_CERT_get_data(cert, PKI_X509_DATA_SUBJECT), 0, flags);
+	}
+
+	BIO_write(bio, "\0", 1);
+
+	len = (size_t)BIO_get_mem_data(bio, &buf);
+	ptr = BUF_strndup(buf, len);
+
+end:
+	if(bio) BIO_free(bio);
+
+	return(ptr);
+}
+
+int OCSPD_build_ca_list ( OCSPD_CONFIG *handler,
+			PKI_CONFIG_STACK *ca_conf_sk) {
+
+	int i = 0;
+	PKI_STACK *ca_list = NULL;
+
+	PKI_log_debug("Building CA List");
+
+	if ( !ca_conf_sk ) {
+		PKI_log( PKI_LOG_ERR, "No stack of ca configs!");
+		return ( PKI_ERR );
+	}
+
+	if((ca_list = PKI_STACK_new((void (*))CA_LIST_ENTRY_free)) == NULL ) {
+		PKI_log_err ( "Memory Error");
+	return ( PKI_ERR );
+	}
+
+	for (i = 0; i < PKI_STACK_CONFIG_elements( ca_conf_sk ); i++)
+	{
+		char *tmp_s  = NULL;
+		char *tmp_s2 = NULL;
+		URL *tmp_url = NULL;
+		PKI_X509_CERT *tmp_cert = NULL;
+
+		CA_LIST_ENTRY *ca = NULL;
+		PKI_CONFIG *cnf = NULL;
+
+		int idx;
+		PKI_X509_EXTENSION_VALUE *ext = NULL;
+		ASN1_OCTET_STRING *oct_str = NULL;
+		const unsigned char *p = NULL;
+		const X509V3_EXT_METHOD *method = NULL;
+
+		/* Get the current Configuration file */
+		cnf = PKI_STACK_CONFIG_get_num( ca_conf_sk, i );
+		if (!cnf) continue;
+
+		/* Get the CA cert from the cfg file itself */
+		if((tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/caCertValue" )) == NULL )
+		{
+			tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/caCertUrl" );
+			if( tmp_s == NULL )
+			{
+				PKI_log( PKI_LOG_ERR, "No CA cert url given" );
+				return ( PKI_ERR );
+			}
+
+			/* Get the CA parsed url */
+			if((tmp_url = URL_new( tmp_s )) == NULL )
+			{
+				/* Error, can not parse url data */
+				PKI_log( PKI_LOG_ERR, "Can not parse CA cert url (%s)", tmp_s);
+				PKI_Free(tmp_s);
+				return ( PKI_ERR );
+			}
+
+			if((tmp_cert = PKI_X509_CERT_get_url(tmp_url, NULL, NULL ))== NULL)
+			{
+				PKI_log_err("Can not get CA cert from (%s)", tmp_url->url_s);
+				URL_free (tmp_url);
+				PKI_Free(tmp_s);
+				return ( PKI_ERR );
+			}
+			PKI_log(PKI_LOG_INFO, "Got CA cert from (%s)", tmp_url->url_s);
+			PKI_Free(tmp_s);
+		}
+		else
+		{
+			PKI_X509_CERT_STACK *cc_sk = NULL;
+			PKI_MEM *mm = NULL;
+
+			if((mm = PKI_MEM_new_null()) == NULL )
+			{
+				PKI_Free(tmp_s);
+				return ( PKI_ERR );
+			}
+
+			PKI_MEM_add ( mm, tmp_s, strlen(tmp_s));
+
+			if((cc_sk=PKI_X509_CERT_STACK_get_mem(mm, NULL)) == NULL )
+			{
+				PKI_log_err ( "Can not parse cert from /caConfig/caCertValue");
+				PKI_Free(tmp_s);
+				return ( PKI_ERR );
+			}
+
+			if ((tmp_cert = PKI_STACK_X509_CERT_pop( cc_sk )) == NULL )
+			{
+				PKI_log_err ( "No elements on stack from /caConfig/caCertValue");
+
+				PKI_STACK_X509_CERT_free_all(cc_sk);
+				PKI_Free(tmp_s);
+				return ( PKI_ERR );
+			}
+
+			PKI_STACK_X509_CERT_free ( cc_sk );
+			PKI_Free(tmp_s);
+		}
+
+		/* OCSPD create the CA entry */
+		if ((ca = CA_LIST_ENTRY_new()) == NULL )
+		{
+			PKI_log_err ( "CA List structure init error");
+
+			/* remember to do THIS!!!! */
+			if( tmp_url ) URL_free ( tmp_url );
+			if( tmp_cert ) PKI_X509_CERT_free ( tmp_cert );
+			return ( PKI_ERR );
+		}
+
+		ca->cert_status_default = -1;
+
+		ca->ca_cert = tmp_cert;
+		tmp_cert = NULL;
+
+		if( (ca->ca_cn = OCSPD_get_cn(ca->ca_cert, handler->log_stats_dn)) == NULL)
+		{
+			PKI_log_err ("Cannot extract subject CN from certificate\n");
+			return(PKI_ERR);
+		}
+
+		ca->ca_url = tmp_url;
+		tmp_url = NULL;
+
+		ca->ca_id = PKI_CONFIG_get_value( cnf, "/caConfig/name" );
+
+
+		PKI_RWLOCK_init ( &ca->single_crl_lock );
+
+		if( (ca->crl_data = PKI_Malloc(sizeof(CRL_DATA)) ) == NULL)
+		{
+			PKI_log_err ("PKI_Malloc(CRL_DATA) failed (%d bytes)\n", sizeof(CRL_DATA));
+			return(-1);
+		}
+
+		ca->sk_cid = CA_ENTRY_CERTID_new_sk ( ca->ca_cert, handler->issuerHashDigest );
+		if(ca->sk_cid == NULL) {
+			PKI_log_err ( "Cannot build CA CertIDs");
+			return ( PKI_ERR );
+		}
+
+		/* checkIssuedByCA */
+		if((tmp_s = PKI_CONFIG_get_value( cnf, 
+					"/caConfig/checkIssuedByCA")) != NULL ) {
+
+			int n, m;
+
+			if (strncmp_nocase(tmp_s, "y", 1) == 0) {
+				ca->check_issued_by_ca = 1;
+				PKI_log(PKI_LOG_INFO, "Check - 'if the requested certificates were issued by our configured CA' - is enabled");
+			}
+			else
+				PKI_log(PKI_LOG_INFO, "Check - 'if the requested certificates were issued by our configured CA' - is disabled");
+			PKI_Free ( tmp_s );
+
+
+			/* Database Options */
+			if((tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/dbUrl")) != NULL ) {
+
+				if(strchr(tmp_s, '?')) {
+
+						PKI_log_err ( "Old dbURL-Scheme with attributes (after '?') no longer supported. Please use 'dbAttr' instead." );
+						CA_LIST_ENTRY_free ( ca );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+
+				} else {
+
+					// New URL scheme. Attribute configuration is sparated and will appended here
+					/* dbAttr */
+					if((tmp_s2 = PKI_CONFIG_get_value( cnf, "/caConfig/dbAttr")) != NULL ) {
+
+						if(parse_stats_items(tmp_s2, &(ca->db_attr_flags), tmp_s, &(ca->db_url)) != 0) {
+        
+							PKI_log_err ( "dbAttr not parsable. No attributes can be retrieved from database." );
+							CA_LIST_ENTRY_free ( ca );
+							PKI_Free(tmp_s);
+							PKI_Free(tmp_s2);
+							return ( PKI_ERR );
+						}
+						PKI_log_debug ( "CA->dbUrl: %s.", ca->db_url->url_s );
+					} else {
+						if((ca->db_url = URL_new ( tmp_s )) == NULL ) {
+							PKI_log_err ( "Database Url not parsable (%s)", tmp_s );
+							CA_LIST_ENTRY_free ( ca );
+							PKI_Free(tmp_s);
+							return ( PKI_ERR );
+						}
+					}
+					PKI_Free(tmp_s2);
+				}
+
+				PKI_Free(tmp_s);
+
+				// count number of attributes that shall be returned from the SQL query
+				if(ca->db_url->attrs != NULL) {
+					ca->num_db_attr = 1;
+					tmp_s = ca->db_url->attrs;
+					while( (tmp_s = strchr(tmp_s, ',')) != NULL) {
+						ca->num_db_attr++;
+						tmp_s++;
+					}
+				}
+			}
+
+
+			/* Database Options */
+			/* Here an alternative dbUrl can be configured. It will be used if the first one is unavailable */
+			if((tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/dbUrl2")) != NULL ) {
+
+				// New URL scheme. Attribute configuration is sparated and will appended here
+				/* dbAttr */
+				if((tmp_s2 = PKI_CONFIG_get_value( cnf, "/caConfig/dbAttr")) != NULL ) {
+
+					if(parse_stats_items(tmp_s2, &(ca->db_attr_flags), tmp_s, &(ca->db_url2)) != 0) {
+        
+						PKI_log_err ( "dbAttr not parsable. No attributes can be retrieved from database." );
+						CA_LIST_ENTRY_free ( ca );
+						PKI_Free(tmp_s);
+						PKI_Free(tmp_s2);
+						return ( PKI_ERR );
+					}
+					PKI_log_debug ( "CA->dbUrl2: %s.", ca->db_url2->url_s );
+				} else {
+					if((ca->db_url2 = URL_new ( tmp_s )) == NULL ) {
+						PKI_log_err ( "Database Url not parsable (%s)", tmp_s );
+						CA_LIST_ENTRY_free ( ca );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+				}
+
+				PKI_Free(tmp_s2);
+				PKI_Free(tmp_s);
+			}
+
+
+			/* Database connection timeout in seconds*/
+			if ((tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/dbTimeout")) != NULL)
+			{
+				ca->db_ctimeout = atoi(tmp_s);
+				PKI_Free(tmp_s);
+			}
+
+
+			/* revocationCheckValue */
+			if((tmp_s = PKI_CONFIG_get_value( cnf, 
+				"/caConfig/revocationCheckValue")) != NULL ) {
+
+				PKI_log( PKI_LOG_INFO, "/caConfig/revocationCheckValue found");
+				if((ca->revocation_check = (REVOCATION_CHECK *) 
+						PKI_Malloc ( sizeof (REVOCATION_CHECK))) == NULL) {
+					PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+					PKI_Free(tmp_s);
+					return ( PKI_ERR );
+				}
+
+				if(*tmp_s == '!') {
+					ca->revocation_check->negate = 1;
+					ca->revocation_check->nval   = 1;
+					if( (ca->revocation_check->value = PKI_Malloc ( sizeof (char *))) == NULL) {
+						PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+
+					if( (ca->revocation_check->value[0] = strdup(tmp_s+1)) == NULL) {
+						PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+				} else {
+					char *items = tmp_s;
+					char *next;
+					size_t num = 0;
+
+					// first count the number of available check values
+					while(items)
+					{
+						if( (next = strchr(items, ',')) != NULL)
+							next++;
+
+						num++;
+						items = next;
+					}
+
+					// allocate base array
+					if( (ca->revocation_check->value = PKI_Malloc ( num * sizeof (char *))) == NULL) {
+						PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+
+					// then copy each value to our array
+					ca->revocation_check->nval = num;
+					num = 0;
+					items = tmp_s;
+					while(items)
+					{
+						if( (next = strchr(items, ',')) != NULL)
+							*next++ = 0;
+
+						PKI_log( PKI_LOG_INFO, "Item: %s", items);
+						if( (ca->revocation_check->value[num++] = strdup(items)) == NULL) {
+							PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+							PKI_Free(tmp_s);
+							return ( PKI_ERR );
+						}
+
+						items = next;
+					}
+				}
+				PKI_Free(tmp_s);
+			}
+
+			/* fingerprint hash algorithm */
+			if((tmp_s = PKI_CONFIG_get_value( cnf, 
+					"/caConfig/CAfingerprintHashAlgo")) == NULL ) {
+				if( (tmp_s = strdup((const char *)"SHA1") ) == NULL) {
+					PKI_log_err ( "Cannot create CAfingerprintHashAlgo" );
+					CA_LIST_ENTRY_free ( ca );
+					return ( PKI_ERR );
+				}
+			}
+
+			/* calculate CA fingerprint */
+			ca->ca_cert_digest = PKI_X509_CERT_fingerprint_by_name(ca->ca_cert, tmp_s);
+			PKI_Free(tmp_s);
+
+			if(ca->ca_cert_digest == NULL) {
+				PKI_log_err ( "Cannot generate fingerprint for CA certificate for CA [%s]", ca->ca_id);
+				CA_LIST_ENTRY_free ( ca );
+				return ( PKI_ERR );
+			}
+
+			/* convert to string value */
+			for(n = 0, m = 0; n < ca->ca_cert_digest->size; n++) {
+				ca->ca_cert_digest_str[m]  = (char) ((ca->ca_cert_digest->digest[n] & 0xf0) >> 4);
+				if(ca->ca_cert_digest_str[m] < 10)
+					ca->ca_cert_digest_str[m] = (char) ((int) ca->ca_cert_digest_str[m] + 0x30);
+				else
+					ca->ca_cert_digest_str[m] = (char) ((int) ca->ca_cert_digest_str[m] + 0x57);
+				m++;
+
+				ca->ca_cert_digest_str[m]  = (char) (ca->ca_cert_digest->digest[n] & 0x0f);
+				if(ca->ca_cert_digest_str[m] < 10)
+					ca->ca_cert_digest_str[m] = (char) ((int) ca->ca_cert_digest_str[m] + 0x30);
+				else
+					ca->ca_cert_digest_str[m] = (char) ((int) ca->ca_cert_digest_str[m] + 0x57);
+				m++;
+			}
+			ca->ca_cert_digest_str[m] = 0;
+
+			/* Issuer DN Options */
+			if((tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/issuerNameOptions")) != NULL ) {
+				if(!set_name_ex(&ca->issuer_dn_nmflag, tmp_s)) {
+					PKI_log_err ( "Issuer DN options not parsable (%s)", tmp_s );
+					CA_LIST_ENTRY_free ( ca );
+					PKI_Free(tmp_s);
+					return ( PKI_ERR );
+				}
+				PKI_Free(tmp_s);
+			}
+			else {
+				(void)set_name_ex(&ca->issuer_dn_nmflag, 
+					"esc_2253,esc_ctrl,esc_msb,utf8,dump_nostr,dump_unknown,dump_der,sep_comma_plus,sname");
+			}
+
+			ca->db_column_ca_fingerprint = PKI_CONFIG_get_value ( cnf, 
+					"/caConfig/dbColumnNameCaFingerprint" );
 
-	return( NULL );
-}
+			ca->db_column_issuer_name_hash = PKI_CONFIG_get_value ( cnf, 
+					"/caConfig/dbColumnNameIssuerNameHash" );
 
+			ca->db_column_subject_key_identifier = PKI_CONFIG_get_value ( cnf, 
+					"/caConfig/dbColumnNameSubjectKeyIdentifier" );
 
-int OCSPD_build_ca_list ( OCSPD_CONFIG *handler,
-			PKI_CONFIG_STACK *ca_conf_sk) {
+			ca->db_column_serial_number = PKI_CONFIG_get_value ( cnf, 
+					"/caConfig/dbColumnNameSerialNumber" );
 
-	int i = 0;
-	PKI_STACK *ca_list = NULL;
+			ca->db_column_issuer_dn = PKI_CONFIG_get_value ( cnf, 
+					"/caConfig/dbColumnNameIssuerDN" );
 
-	PKI_log_debug("Building CA List");
 
-	if ( !ca_conf_sk ) {
-		PKI_log( PKI_LOG_ERR, "No stack of ca configs!");
-		return ( PKI_ERR );
-	}
+			/* certStatus */
+			if( (ca->db_column_cert_status = PKI_CONFIG_get_value ( cnf, 
+					"/caConfig/dbColumnNameCertStatus" )) ) {
 
-	if((ca_list = PKI_STACK_new((void (*))CA_LIST_ENTRY_free)) == NULL ) {
-		PKI_log_err ( "Memory Error");
-	return ( PKI_ERR );
-	}
+				/* certStatusCheckValue */
+				if((tmp_s = PKI_CONFIG_get_value( cnf, 
+					"/caConfig/certStatusCheckValue")) == NULL )
+				{
+					PKI_log_err ( "dbColumnNameCertStatus configured but no certStatusCheckValue given", tmp_s );
+					return ( PKI_ERR );
+				}
 
-	for (i = 0; i < PKI_STACK_CONFIG_elements( ca_conf_sk ); i++)
-	{
-		char *tmp_s = NULL;
-		URL *tmp_url = NULL;
-		PKI_X509_CERT *tmp_cert = NULL;
+				if(*tmp_s == '!')
+				{
+					memmove(tmp_s, tmp_s + 1, strlen(tmp_s + 1)); // Delete leading '!'
+					ca->cert_status_check_value = tmp_s;
 
-		CA_LIST_ENTRY *ca = NULL;
-		PKI_CONFIG *cnf = NULL;
+					/* now we have to append a '!' to the dbColumnNameCertStatus to "modify"
+					 * the SQL statement that is built at a later time */
+					tmp_s = ca->db_column_cert_status;
+					if( (ca->db_column_cert_status = PKI_Malloc (strlen(tmp_s)) + 2) == NULL) {
+						PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
 
-		/* Get the current Configureation file */
-		cnf = PKI_STACK_CONFIG_get_num( ca_conf_sk, i );
-		if (!cnf) continue;
+					memcpy(ca->db_column_cert_status, tmp_s, strlen(tmp_s));
+					ca->db_column_cert_status[strlen(tmp_s)] = '!';
+					PKI_Free(tmp_s);
+				}
+				else
+					ca->cert_status_check_value = tmp_s;
+				
+			}
 
-		/* Get the CA cert from the cfg file itself */
-		if((tmp_s = PKI_CONFIG_get_value( cnf, "/caConfig/caCertValue" )) == NULL )
-		{
-			/* Get the CA parsed url */
-			if((tmp_url = URL_new( PKI_CONFIG_get_value( cnf, "/caConfig/caCertUrl" ))) == NULL )
+			/* certHashDigestAlgorithm */
+			if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/certHashDigestAlgorithm" )) != NULL)
 			{
-				/* Error, can not parse url data */
-				PKI_log( PKI_LOG_ERR, "Can not parse CA cert url (%s)", 
-					PKI_CONFIG_get_value(cnf, "/caConfig/caCertUrl"));
-
-				continue;
+				ca->cert_hash_digest = PKI_DIGEST_ALG_get_by_name( tmp_s );
+				if (!ca->cert_hash_digest) 
+				{
+					PKI_log_err("Can not parse certHashDigestAlgorithm: %s", tmp_s);
+					PKI_Free(tmp_s);
+					return ( PKI_ERR );
+				}
+    
+				PKI_log_debug("Selected certHashDigestAlgorithm: %s", tmp_s);
+				
+				PKI_Free(tmp_s);
 			}
-
-			if((tmp_cert = PKI_X509_CERT_get_url(tmp_url, NULL, NULL ))== NULL)
+			else
 			{
-				PKI_log_err("Can not get CA cert from (%s)", tmp_url);
-				URL_free (tmp_url);
-
-				continue;
+				ca->cert_hash_digest = (EVP_MD *)EVP_sha256();
 			}
-		}
-		else
-		{
-			PKI_X509_CERT_STACK *cc_sk = NULL;
-			PKI_MEM *mm = NULL;
 
-			if((mm = PKI_MEM_new_null()) == NULL )
+			/* archiveCutoff  */
+			if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/archiveCutoff" )) != NULL)
 			{
+
+				if (strncmp_nocase(tmp_s, "y", 1) == 0)
+				{
+					ASN1_TIME *t = NULL;
+      
+					t = PKI_X509_CERT_get_data(ca->ca_cert, PKI_X509_DATA_NOTBEFORE);
+					if( (ca->archive_cutoff = PKI_Malloc((size_t)t->length + 3) ) == NULL)
+					{
+						PKI_log_err("PKI_Malloc(%d bytes) failed", t->length + 1);
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+        
+					ca->archive_cutoff[0] = '2';
+					ca->archive_cutoff[1] = '0';
+					memcpy(ca->archive_cutoff + 2, t->data, (size_t)t->length);
+					PKI_log_debug("archiveCutoff enabled: %s!", ca->archive_cutoff);
+				}
+
 				PKI_Free(tmp_s);
-				continue;
 			}
 
-			PKI_MEM_add ( mm, tmp_s, strlen(tmp_s));
-
-			if((cc_sk=PKI_X509_CERT_STACK_get_mem(mm, NULL)) == NULL )
+			/* nextUpdateExpired  */
+			if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/nextUpdateExpired" )) != NULL)
 			{
-				PKI_log_err ( "Can not parse cert from /caConfig/caCertValue");
+				if (strncmp_nocase(tmp_s, "y", 1) == 0)
+				{
+					if((ca->next_update_expired = (PKI_TIME *) ASN1_GENERALIZEDTIME_new()) == NULL )
+					{
+						PKI_log_err("ASN1_GENERALIZEDTIME_new(nextUpdateExpired) failed");
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+
+					if(!ASN1_GENERALIZEDTIME_set_string(ca->next_update_expired, "99991231235959Z"))
+					{
+						PKI_log_err("ASN1_GENERALIZEDTIME_set_string(nextUpdateExpired) failed");
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+				}
 				PKI_Free(tmp_s);
-
-				continue;
 			}
 
-			if ((tmp_cert = PKI_STACK_X509_CERT_pop( cc_sk )) == NULL )
+			/* certType */
+			if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/certType" )) != NULL)
 			{
-				PKI_log_err ( "No elements on stack from /caConfig/caCertValue");
+				ca->cert_type = tmp_s;
+				tmp_s = NULL;
+			}
 
-				PKI_STACK_X509_CERT_free_all(cc_sk);
-				PKI_Free(tmp_s);
 
-				continue;
+			/* crlFallback */
+			if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/crlFallback")) != NULL)
+			{
+				if (strncmp_nocase(tmp_s, "y", 1) == 0) 
+				{
+					ca->crl_fallback = 1;
+				}
+    
+				PKI_Free(tmp_s);
 			}
 
-			PKI_STACK_X509_CERT_free ( cc_sk );
-			PKI_Free(tmp_s);
 		}
 
-		/* OCSPD create the CA entry */
-		if ((ca = CA_LIST_ENTRY_new()) == NULL )
+		/* Extract subjectKeyIdentifier from issuer certificate */
+		if( (idx = X509_get_ext_by_NID(ca->ca_cert->value, NID_subject_key_identifier, -1)) < 0)
 		{
-			PKI_log_err ( "CA List structure init error");
+			PKI_log_debug ("No subjectKeyIdentifier found in issuer certificate (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
+		}
 
-			/* remember to do THIS!!!! */
-			if( tmp_url ) URL_free ( tmp_url );
-			if( tmp_cert ) PKI_X509_CERT_free ( tmp_cert );
+		PKI_log_debug ( "Found SKI for CA '%s'", ca->ca_id);
 
-			continue;
+		if( (ext = X509_get_ext(ca->ca_cert->value, idx) ) == NULL)
+		{
+			PKI_log_debug ("Cannot extract subjectKeyIdentifier from issuer certificate (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
 		}
 
-		ca->ca_cert = tmp_cert;
-		tmp_cert = NULL;
+		if(ext->value == NULL)
+		{
+			PKI_log_err ( "subjectKeyIdentifier does not contain value as ASN1_OCTET_STRING (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
+		}
 
-		ca->ca_url = tmp_url;
-		tmp_url = NULL;
+		if (!(method = X509V3_EXT_get(ext)))
+		{
+			PKI_log_err ( "X509V3_EXT_get(subjectKeyIdentifier) failed (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
+		}
 
-		ca->ca_id = PKI_CONFIG_get_value( cnf, "/caConfig/name" );
-		ca->cid = CA_ENTRY_CERTID_new ( ca->ca_cert, handler->digest );
+		p = ext->value->data;
+		if (!method->it)
+		{
+			PKI_log_err ( "subjectKeyIdentifier object does not contain ASN1 item structure (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
+		}
 
-		/* Get the CRL URL and the CRL itself */
-		if((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/crlUrl")) == NULL)
+		if( (oct_str = (ASN1_OCTET_STRING *) ASN1_item_d2i(NULL, &p, ext->value->length,
+				ASN1_ITEM_ptr(method->it)) ) == NULL)
 		{
-			PKI_STACK *cdp_sk = NULL;
+			PKI_log_err ( "Cannot extract value from subjectKeyIdentifier structure (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
+		}
 
-			/* Now let's get it from PRQP */
+		PKI_log_hexdump(PKI_LOG_DEBUG, "SKI RAW:", (int)oct_str->length, (unsigned char *) oct_str->data);
 
-			/* Now from the Certificate */
-			
-			if((cdp_sk = PKI_X509_CERT_get_cdp (ca->ca_cert)) ==NULL)
-			{
-				// No source for the CRL Distribution Point
-				PKI_log_err ( "ERROR::Can not find the CDP for %s, skipping CA", ca->ca_id );
+		ca->ca_cert_ski = bin_to_string(oct_str->data, (size_t)oct_str->length);
 
-				CA_LIST_ENTRY_free ( ca );
-				continue;
-			}
+		ASN1_OCTET_STRING_free(oct_str);
 
-			while ((tmp_s = PKI_STACK_pop ( cdp_sk )) != NULL)
+		if( ca->ca_cert_ski == NULL)
+		{
+			PKI_log_err ( "Converting subjectKeyIdentifer to string failed (CA = %s)", ca->ca_id);
+			return ( PKI_ERR );
+		}
+		PKI_log_debug("SKI for CA '%s': %s", ca->ca_id, ca->ca_cert_ski);
+
+		if(!ca->check_issued_by_ca || !ca->revocation_check || ca->crl_fallback)
+		{
+			/* Get the CRL URL and the CRL itself */
+			if((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/crlUrl")) == NULL)
 			{
-				if ((ca->crl_url = URL_new ( tmp_s )) == NULL )
+				PKI_STACK *cdp_sk = NULL;
+                
+				/* Now let's get it from PRQP */
+                
+				/* Now from the Certificate */
+				
+				if((cdp_sk = PKI_X509_CERT_get_cdp (ca->ca_cert)) ==NULL)
 				{
-					PKI_log_err( "URL %s not in the right format!");
+					// No source for the CRL Distribution Point
+					PKI_log_err ( "ERROR::Can not find the CDP for %s, skipping CA", ca->ca_id );
+                
 					CA_LIST_ENTRY_free ( ca );
-					continue;
+					return ( PKI_ERR );
+				}
+                
+				while ((tmp_s = PKI_STACK_pop ( cdp_sk )) != NULL)
+				{
+					if ((ca->crl_url = URL_new ( tmp_s )) == NULL )
+					{
+						PKI_log_err( "URL %s not in the right format!");
+						CA_LIST_ENTRY_free ( ca );
+						return ( PKI_ERR );
+					}
+					else if( tmp_s ) PKI_Free ( tmp_s );
+                
+					break;
 				}
-				else if( tmp_s ) PKI_Free ( tmp_s );
-
-				break;
 			}
-		}
-		else
-		{
-			PKI_log_debug("Got CRL Url -> %s", tmp_s );
-
-			if((ca->crl_url = URL_new ( tmp_s )) == NULL )
+			else
 			{
-				PKI_log_err ("Error Parsing CRL URL [%s] for CA [%s]", ca->ca_id, tmp_s);
-
-				CA_LIST_ENTRY_free ( ca );
+				PKI_log_debug("Got CRL Url -> %s", tmp_s );
+                
+				if((ca->crl_url = URL_new ( tmp_s )) == NULL )
+				{
+					PKI_log_err ("Error Parsing CRL URL [%s] for CA [%s]", ca->ca_id, tmp_s);
+                
+					CA_LIST_ENTRY_free ( ca );
+					PKI_Free(tmp_s);
+					return ( PKI_ERR );
+				}
+                
 				PKI_Free(tmp_s);
-
-				continue;
 			}
-
-			PKI_Free(tmp_s);
+                
+			if(OCSPD_load_crl ( ca, handler ) == PKI_ERR )
+			{
+				PKI_log_err ( "Can not get CRL for %s", ca->ca_id);
+				CA_LIST_ENTRY_free ( ca );
+				return ( PKI_ERR );
+			}
 		}
-
-		if(OCSPD_load_crl ( ca, handler ) == PKI_ERR )
+		/* Perform some sanity checks */
+		if(ca->num_db_attr == 0 && ca->crl_url == NULL)
 		{
-			PKI_log_err ( "Can not get CRL for %s", ca->ca_id);
+			PKI_log_err ( "No 'dbAttr' and no 'crlUrl' are configured. Please check configuration in '%s'.", cnf->URL);
 			CA_LIST_ENTRY_free ( ca );
-
-			continue;
+			return ( PKI_ERR );
 		}
+    
 
 		/* If the Server has a Token to be used with this CA, let's
                    load it */
@@ -548,8 +1738,7 @@
 
 					CA_LIST_ENTRY_free ( ca );
 					PKI_Free(tmp_s);
-
-					continue;
+					return ( PKI_ERR );
 				}
 				else
 				{
@@ -565,7 +1754,12 @@
  			   it to avoid problems with Thread Initialization */
 			ca->server_cert = NULL;
 			ca->token_name = tmp_s;
-			ca->token = PKI_TOKEN_new_null();
+			if ((ca->token = PKI_TOKEN_new_null()) == NULL)
+			{
+				PKI_log( PKI_LOG_ERR, "Memory error for new token");
+				CA_LIST_ENTRY_free ( ca );
+				return ( PKI_ERR );
+			}
 
 			if ((tmp_s = PKI_CONFIG_get_value ( cnf, "/caConfig/pkiConfigDir" )) != NULL) {
 				ca->token_config_dir = strdup( tmp_s );
@@ -577,12 +1771,45 @@
 			}
 		}
 
-		if((tmp_s = PKI_CONFIG_get_value ( cnf, "/caConfig/caCompromised" )) == NULL) {
-			ca->compromised = 0;
-		}
-		else
-		{
-			ca->compromised = atoi(tmp_s);
+		if((tmp_s = PKI_CONFIG_get_value ( cnf, "/caConfig/caCompromised" )) != NULL) {
+
+			if (strncmp_nocase(tmp_s, "y", 1) == 0)
+			{
+				ca->compromised = 1;
+				PKI_log(PKI_LOG_INFO, "caCompromised option set");
+
+				PKI_Free(tmp_s);
+
+				if((tmp_s = PKI_CONFIG_get_value ( cnf, "/caConfig/caCompromisedDate" )) != NULL) {
+      
+					char *s = NULL;
+					PKI_TIME *t = PKI_TIME_new(0);
+
+					/* The revocation date of the CA must be entered manually. */
+					if(t == NULL)
+					{
+						PKI_log_err ( "PKI_TIME_new() failed for caCompromisedDate");
+						CA_LIST_ENTRY_free ( ca );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+
+					if(!ASN1_TIME_set_string(t, tmp_s))
+					{
+						PKI_log_err ( "Converting caCopromisedDate failed! Format must be YYYYMMDDHHMMSSZ");
+						CA_LIST_ENTRY_free ( ca );
+						PKI_Free(tmp_s);
+						return ( PKI_ERR );
+					}
+
+					s = PKI_TIME_get_parsed(t);
+					PKI_log(PKI_LOG_ALWAYS, "caCompromisedDate: %s", s);
+					PKI_Free(s);
+					ca->compromised_date = t;
+					t = NULL;
+				}
+			}
+    
 			PKI_Free(tmp_s);
 		}
 
@@ -611,6 +1838,45 @@
 			ca->response_id_type = PKI_X509_OCSP_RESPID_TYPE_BY_NAME;
 		}
 
+		/* ignoreReasonCode */
+		if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/ignoreReasonCode")) != NULL)
+		{
+			if (strncmp_nocase(tmp_s, "y", 1) == 0) 
+			{
+				ca->ignore_reason_code = 1;
+			}
+
+			PKI_Free(tmp_s);
+		}
+
+		/* certStatusDefaultResponse */
+		if ((tmp_s = PKI_CONFIG_get_value(cnf, "/caConfig/certStatusDefaultResponse")) != NULL)
+		{
+			if (strcmp_nocase(tmp_s, "good") == 0) 
+			{
+				ca->cert_status_default = V_OCSP_CERTSTATUS_GOOD;
+			}
+#if 0
+			/* Don't support 'revoked' as it requires revocationTime */
+			else if (strcmp_nocase(tmp_s, "revoked") == 0) 
+			{
+				ca->cert_status_default = V_OCSP_CERTSTATUS_REVOKED;
+			}
+#endif
+			else if (strcmp_nocase(tmp_s, "unknown") == 0) 
+			{
+				ca->cert_status_default = V_OCSP_CERTSTATUS_UNKNOWN;
+			}
+			else
+			{
+				PKI_log(PKI_LOG_ALWAYS,
+					"Ignoring unsupported certStatusDefaultResponse '%s' for %s",
+					tmp_s, ca->ca_id);
+			}
+
+			PKI_Free(tmp_s);
+		}
+
 		// Now let's add the CA_LIST_ENTRY to the list of configured CAs
 		PKI_STACK_push ( ca_list, ca );
 
@@ -633,46 +1899,57 @@
 		return PKI_ERR;
 	}
 
-	if ( ca->crl ) PKI_X509_CRL_free ( ca->crl );
+	if ( ca->crl_data->crl ) PKI_X509_CRL_free ( ca->crl_data->crl );
 
-	if (( ca->crl = PKI_X509_CRL_get_url ( ca->crl_url, 
+	if (( ca->crl_data->crl = PKI_X509_CRL_get_url ( ca->crl_url, 
 						NULL, NULL )) == NULL ) {
 		PKI_log_err ("Failed loading CRL for %s", ca->ca_id );
 		return PKI_ERR;
 	}
 
+	if(conf->crl_check_mtime) {
+		struct stat st;
+
+		if(stat(ca->crl_url->addr, &st) == -1) {
+			PKI_log_err ("Cannot access CRL %s (%s)", 
+				ca->crl_url->addr, strerror(errno) );
+		}
+		else
+			ca->crl_data->mtime = st.st_mtime;
+	}
+  
 	/* Let's check the CRL against the CA certificate */
-	if( (ret = check_crl( ca->crl, ca->ca_cert, conf )) < 1 ) {
+	if( (ret = check_crl( ca->crl_data->crl, ca->ca_cert, &ca->single_crl_lock, conf )) < 1 ) {
 		PKI_log_err( "CRL/CA check error [ %s:%d ]",
 						ca->ca_id, ret );
 		return PKI_ERR;
 	}
 
 	/* Now we copy the lastUpdate and nextUpdate fields */
-	if( ca->crl ) {
-		ca->lastUpdate = PKI_TIME_dup(
-			PKI_X509_CRL_get_data (ca->crl, 
+	if( ca->crl_data->crl ) {
+		ca->crl_data->lastUpdate = PKI_TIME_dup(
+			PKI_X509_CRL_get_data (ca->crl_data->crl, 
 				PKI_X509_DATA_LASTUPDATE));
 
-		ca->nextUpdate = PKI_TIME_dup (
-			PKI_X509_CRL_get_data (ca->crl,
+		ca->crl_data->nextUpdate = PKI_TIME_dup (
+			PKI_X509_CRL_get_data (ca->crl_data->crl,
 				PKI_X509_DATA_NEXTUPDATE ));
 	}
 
-	if((ca->crl_status = check_crl_validity(ca, conf )) == CRL_OK ) {
+	if((ca->crl_data->crl_status = check_crl_validity(ca->crl_data, &ca->single_crl_lock, conf, ca->ca_id )) == CRL_OK ) {
 		if(conf->verbose) PKI_log( PKI_LOG_INFO, "CRL for %s is Valid", 
 				ca->ca_id );
 	} else {
 		PKI_log_err ( "CRL for %s has ERRORS (%d)", ca->ca_id, 
-						ca->crl_status );
+						ca->crl_data->crl_status );
 	}
 
 	/* Let's get the CRLs entries, if any */
-	if( ocspd_build_crl_entries_list ( ca, ca->crl ) == NULL ) { 
+	if( (ca->crl_data->crl_list = ocspd_build_crl_entries_list ( ca->crl_data->crl, ca->ca_id ) ) == NULL ) { 
 		PKI_log(PKI_LOG_ALWAYS, "No CRL Entries for %s", ca->ca_id );
 	};
 
-	if(conf->verbose) PKI_log( PKI_LOG_ALWAYS, "CRL loaded for %s", ca->ca_id );
+	PKI_log( PKI_LOG_ALWAYS, "CRL loaded for %s (%d entries)", ca->ca_id, sk_X509_REVOKED_num(ca->crl_data->crl_list));
 
 	return PKI_OK;
 }
@@ -701,49 +1978,37 @@
 							NULL, NULL );
 		}
 
-		/*
-		if(!ca->cert || !sk_X509_num(ca->cert)) {
-			syslog(LOG_ERR, "Error loading CA URL data.");
-			continue;
-		} else {
-			if(conf->verbose)
-				syslog( LOG_INFO,
-					"CA CERT for %s loaded successfully.",
-					ca->ca_id );
-		}
-		*/
 		if( !ca->ca_cert ) {
 			if( ca->ca_url && ca->ca_url->url_s ) {
 			   PKI_log_err ( "Can not load CA cert from %s",
 				ca->ca_url->url_s);
 			} else {
 				PKI_log_err ( "Can not load CA cert!");
-				continue;
+				return ( PKI_ERR );
 			}
 		} else {
 			PKI_log( PKI_LOG_INFO, " CA cert for %s loaded ok",
 					ca->ca_id );
 		}
 
-		if((ca->cid = CA_ENTRY_CERTID_new ( ca->ca_cert,
-						conf->digest)) == NULL ) {
+		ca->sk_cid = CA_ENTRY_CERTID_new_sk ( ca->ca_cert, conf->issuerHashDigest );
+		if(ca->sk_cid == NULL ) {
 			PKI_log_err( "CA List structure init error (CERTID).");
-			continue;
+			return ( PKI_ERR );
 		}
-
 	}
 
 	return 1;
 }
 
-STACK_OF(X509_REVOKED) *ocspd_build_crl_entries_list ( CA_LIST_ENTRY *ca, PKI_X509_CRL *crl )
+STACK_OF(X509_REVOKED) *ocspd_build_crl_entries_list ( PKI_X509_CRL *crl, char *ca_id )
 {
 	long rev_num = 0;
 
 	STACK_OF(X509_REVOKED) *ret = NULL;
 	PKI_X509_CRL_VALUE *crl_val = NULL;
 
-	if ( !ca || !crl || !crl->value ) 
+	if ( !crl || !crl->value || !ca_id ) 
 	{
 		return NULL;
 	}
@@ -754,21 +2019,18 @@
 	rev_num = sk_X509_REVOKED_num(ret);
 
 	// if( ocspd_conf->verbose )
-	PKI_log( PKI_LOG_INFO, "INFO::CRL::%ld Entries [ %s ]", rev_num, ca->ca_id );
-
-	ca->crl_list = ret;
-	ca->entries_num = (unsigned long) rev_num;
+	PKI_log( PKI_LOG_INFO, "INFO::CRL::%ld Entries [ %s ]", rev_num, ca_id );
 
 	if ((rev_num > -1 ) && 
-		(ca->crl_list == NULL))
+		(ret == NULL))
 	{
 		PKI_ERROR( PKI_ERR_MEMORY_ALLOC, NULL );
 		return NULL;
 	}
 
-	sk_X509_REVOKED_sort(ca->crl_list);
+	sk_X509_REVOKED_sort(ret);
 
-	return (ca->crl_list);
+	return (ret);
 }
 
 /* --------------------------- CA_LIST_ENTRY ------------------------- */
@@ -786,36 +2048,86 @@
 	return ( ca );
 }
 
+void CRL_DATA_free ( CRL_DATA *crl_data ) {
+
+	if ( crl_data->crl_list )
+	{
+		X509_REVOKED *r = NULL;
+
+		while ((r = sk_X509_REVOKED_pop ( crl_data->crl_list )) != NULL) 
+		{
+			X509_REVOKED_free ( r );
+		}
+	}
+
+	if ( crl_data->crl ) PKI_X509_CRL_free ( crl_data->crl );
+	if ( crl_data->nextUpdate ) PKI_TIME_free ( crl_data->nextUpdate );
+	if ( crl_data->lastUpdate ) PKI_TIME_free ( crl_data->lastUpdate );
+}
+
+void CRL_DATA_free_all ( CRL_DATA *crl_data ) {
+
+	CRL_DATA_free(crl_data);
+	PKI_Free(crl_data);
+}
+
 void CA_LIST_ENTRY_free ( CA_LIST_ENTRY *ca ) {
 
 	if ( !ca ) return;
 
 	if ( ca->ca_id )
 	{
-		PKI_log(PKI_LOG_INFO, "MEM::Freeing %s CA config", ca->ca_id );
+		PKI_log_debug("MEM::Freeing %s CA config", ca->ca_id );
 		PKI_Free ( ca->ca_id );
 	}
 
 	if ( ca->ca_cert ) PKI_X509_CERT_free ( ca->ca_cert );
-	if ( ca->cid ) CA_ENTRY_CERTID_free ( ca->cid );
-	if ( ca->ca_url ) URL_free ( ca->ca_url );
-	if ( ca->crl_url ) URL_free ( ca->crl_url );
-
-	if ( ca->crl_list )
+	if ( ca->sk_cid )
 	{
-		X509_REVOKED *r = NULL;
+		CA_ENTRY_CERTID *cid;
 
-		while ((r = sk_X509_REVOKED_pop ( ca->crl_list )) != NULL) 
+		while ((cid = sk_CA_ENTRY_CERTID_pop ( ca->sk_cid )) != NULL) 
 		{
-			X509_REVOKED_free ( r );
+			CA_ENTRY_CERTID_free ( cid );
 		}
+		sk_CA_ENTRY_CERTID_free ( ca->sk_cid );
 	}
+	if ( ca->ca_url ) URL_free ( ca->ca_url );
+	if ( ca->ca_cn )  PKI_Free ( ca->ca_cn );
+	if ( ca->crl_url ) URL_free ( ca->crl_url );
+
+	CRL_DATA_free_all(ca->crl_data);
+
+	if ( ca->token_name )                 PKI_Free ( ca->token_name );
+	if ( ca->token )                      PKI_TOKEN_free_void ( ca->token );
 
-	if ( ca->nextUpdate ) PKI_TIME_free ( ca->nextUpdate );
-	if ( ca->lastUpdate ) PKI_TIME_free ( ca->lastUpdate );
+	if ( ca->compromised_date )           PKI_TIME_free ( ca->compromised_date );
+	if ( ca->token_config_dir )           PKI_Free ( ca->token_config_dir );
+	if ( ca->db_url )                     URL_free ( ca->db_url );
+	if ( ca->db_url2 )                    URL_free ( ca->db_url2 );
+	if ( ca->revocation_check )
+	{
+		int i;
+
+		for(i = 0; i < ca->revocation_check->nval; i++)
+			PKI_Free (ca->revocation_check->value[i] );
+		PKI_Free ( ca->revocation_check->value );
+		PKI_Free ( ca->revocation_check );
+	}
+	if ( ca->ca_cert_digest )                   PKI_DIGEST_free ( ca->ca_cert_digest );
+	if ( ca->ca_cert_ski )                      PKI_Free ( ca->ca_cert_ski );
+	if ( ca->db_column_ca_fingerprint )         PKI_Free ( ca->db_column_ca_fingerprint );
+	if ( ca->db_column_issuer_name_hash )       PKI_Free ( ca->db_column_issuer_name_hash );
+	if ( ca->db_column_subject_key_identifier ) PKI_Free ( ca->db_column_subject_key_identifier );
+	if ( ca->db_column_serial_number )          PKI_Free ( ca->db_column_serial_number );
+	if ( ca->db_column_issuer_dn )              PKI_Free ( ca->db_column_issuer_dn );
+	if ( ca->db_column_cert_status )            PKI_Free ( ca->db_column_cert_status );
+	if ( ca->cert_status_check_value )          PKI_Free ( ca->cert_status_check_value );
+	if ( ca->archive_cutoff )                   PKI_Free ( ca->archive_cutoff );
+	if ( ca->next_update_expired )              PKI_TIME_free ( ca->next_update_expired );
+	if ( ca->cert_type )                        PKI_Free ( ca->cert_type );
 
-	if ( ca->token_name ) PKI_Free ( ca->token_name );
-	if ( ca->token ) PKI_TOKEN_free ( ca->token );
+	PKI_RWLOCK_destroy ( &ca->single_crl_lock );
 
 	PKI_Free ( ca );
 
@@ -832,11 +2144,11 @@
 	if (( ret = PKI_Malloc ( sizeof( CA_LIST_ENTRY ) )) == NULL ) return NULL;
 
 	/* Let's get the CA_ENTRY_CERTID */
-	if ((ret->cid = CA_ENTRY_CERTID_new ( x, handler->digest )) == NULL)
-	{
+	ret->sk_cid = CA_ENTRY_CERTID_new_sk ( x, handler->issuerHashDigest );
+	if(ret->sk_cid == NULL ) {
 		CA_LIST_ENTRY_free ( ret );
-		return NULL;
-	}
+		return ( NULL );
+ 	}
 
 	return ret;
 
@@ -844,27 +2156,36 @@
 
 /* ---------------------------- CA_ENTRY_CERTID ------------------------- */
 
-CA_ENTRY_CERTID * CA_ENTRY_CERTID_new ( PKI_X509_CERT *cert, 
-					PKI_DIGEST_ALG * digestAlg ) {
+STACK_OF(CA_ENTRY_CERTID) * CA_ENTRY_CERTID_new_sk ( PKI_X509_CERT *cert, 
+					STACK_OF(EVP_MD) *mds ) {
 
-	CA_ENTRY_CERTID *ret = NULL;
+	int i;
+	STACK_OF(CA_ENTRY_CERTID) *sk_cid = NULL;
+	CA_ENTRY_CERTID *cid = NULL;
 
 	PKI_STRING *keyString = NULL;
 	PKI_DIGEST *keyDigest = NULL;
 
 	PKI_X509_NAME *iName = NULL;
 	PKI_DIGEST *nameDigest = NULL;
+	STACK_OF(EVP_MD) *sk_md = NULL;
+
+
+	PKI_log_debug("Building CA_ENTRY_CERTID stack");
 
 	/* Check for needed info */
 	if ( !cert || !cert->value ) return NULL;
 
-	/* Use SHA1 as default digest algorithm */
-	if ( !digestAlg ) digestAlg = PKI_DIGEST_ALG_SHA1;
+	/* fallback */
+	if ( !mds )
+	{
+		if(!OCSPD_EVP_MD_STACK_add_md(&(sk_md), PKI_DIGEST_ALG_SHA1))
+		{
+			PKI_log_err("Can not add digest algorithm");
+			return (NULL);
+		}
 
-	// Allocate Memory for the CA_ENTRY_CERTID
-	if((ret = PKI_Malloc(sizeof(CA_ENTRY_CERTID))) == NULL) {
-		PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL);
-		goto err;
+		mds = sk_md; // use input parameter as temporary variable
 	}
 
 	/* Retrieves the subject name from the certificate */
@@ -874,57 +2195,96 @@
 		goto err;
 	};
 
-	// Let's build the HASH of the Name
-	if((nameDigest = PKI_X509_NAME_get_digest(iName, digestAlg)) == NULL) {
-		PKI_log_err("Can not get digest string from certificate's subject");
-		goto err;
-	};
-
-	// Assign the new OCTET string tothe nameHash field
-	if (( ret->nameHash = PKI_STRING_new ( PKI_STRING_OCTET,
-			(char *) nameDigest->digest, (ssize_t) nameDigest->size )) == NULL ) {
-		PKI_log_err("Can not assign nameHash to CERTID");
-		goto err;
-	};
-	
 	// Let's get the key bitstring from the certificate
 	if (( keyString = PKI_X509_CERT_get_data( cert, 
 				PKI_X509_DATA_PUBKEY_BITSTRING)) == NULL ) {
 		PKI_log_err("Can not get certificate's pubkey bitstring");
 		goto err;
+	}
+
+	// generate a digest for each given hash algorithm
+	for (i = 0; i < sk_EVP_MD_num(mds); ++i) {
+
+		EVP_MD *md = NULL;
+
+		// Allocate Memory for the CA_ENTRY_CERTID stack
+		if (!sk_cid && (sk_cid = sk_CA_ENTRY_CERTID_new_null()) == NULL) {
+			PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL);
+			goto err;
+		}
+
+		md = sk_EVP_MD_value(mds, i);
+		if(!md)
+			continue;
+
+		PKI_log_debug("===Selected issuerHashDigestAlgorithm: %s", EVP_MD_name(md));
+
+		// Allocate Memory for each CA_ENTRY_CERTID
+		if((cid = PKI_Malloc(sizeof(CA_ENTRY_CERTID))) == NULL) {
+			PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL);
+			goto err;
+		}
+
+		// Let's build the HASH of the Name
+		if((nameDigest = PKI_X509_NAME_get_digest(iName, md)) == NULL) {
+			PKI_log_err("Can not get digest string from certificate's subject");
+			goto err;
+		}
+
+		// Assign the new OCTET string to the nameHash field
+		// remove comment: shouldn't PKI_STRING_new() use size_t ???
+		if (( cid->nameHash = PKI_STRING_new ( PKI_STRING_OCTET,
+				(char *)nameDigest->digest, (ssize_t) nameDigest->size )) == NULL ) {
+			PKI_log_err("Can not assign nameHash to CERTID");
+			goto err;
+		};
 
-	} else {
 		// We build the keyDigest from the keyString
-		if((keyDigest = PKI_STRING_get_digest (keyString, 
-				digestAlg)) == NULL ) {
+		if((keyDigest = PKI_STRING_get_digest (keyString, md)) == NULL ) {
 			PKI_log_err("Can not create new keyDigest from keyString");
 			goto err;
 		};
-	};
 
-	if((ret->keyHash = PKI_STRING_new ( PKI_STRING_OCTET,
-				(char *) keyDigest->digest, (ssize_t) keyDigest->size )) == NULL ) {
-		PKI_log_err("Can not assign keyHash to CERTID");
-		goto err;
-	};
+		// remove comment: shouldn't PKI_STRING_new() use size_t ???
+		if((cid->keyHash = PKI_STRING_new ( PKI_STRING_OCTET,
+						(char *)keyDigest->digest, (ssize_t) keyDigest->size )) == NULL ) {
+			PKI_log_err("Can not assign keyHash to CERTID");
+			goto err;
+		}
 
-	/* Set the Digest Algorithm used */
-	if((ret->hashAlgorithm = PKI_ALGORITHM_new_digest( digestAlg )) == NULL ) {
-		if( ret ) CA_ENTRY_CERTID_free ( ret );
-		PKI_log_err("ERROR, can not create a new hashAlgorithm!");
-		return NULL;
-	};
+		/* Set the Digest Algorithm used */
+		if((cid->hashAlgorithm = PKI_ALGORITHM_new_digest( md )) == NULL ) {
+			PKI_log_err("ERROR, can not create a new hashAlgorithm!");
+			goto err;
+		}
 
-	if ( nameDigest ) PKI_DIGEST_free ( nameDigest );
-	if ( keyDigest  ) PKI_DIGEST_free ( keyDigest );
+		// add entry to our stack
+		if(!sk_CA_ENTRY_CERTID_push(sk_cid, cid)) {
+			PKI_log_err("ERROR, can not create a new hashAlgorithm!");
+			goto err;
+		}
+		cid = NULL;
 
-	return ret;
+		if ( nameDigest ) {
+			PKI_DIGEST_free ( nameDigest );
+			nameDigest = NULL;
+		}
+		if ( keyDigest  ) {
+			PKI_DIGEST_free ( keyDigest );
+			keyDigest = NULL;
+		}
+	}       
 
+	if ( sk_md ) sk_EVP_MD_free(sk_md);
+
+	return sk_cid;
+ 
 err:
 	if ( nameDigest ) PKI_DIGEST_free ( nameDigest );
 	if ( keyDigest  ) PKI_DIGEST_free ( keyDigest );
-
-	if ( ret ) CA_ENTRY_CERTID_free ( ret );
+	if ( sk_cid )     sk_CA_ENTRY_CERTID_free(sk_cid);
+	if ( cid )        CA_ENTRY_CERTID_free ( cid );
+	if ( sk_md )      sk_EVP_MD_free(sk_md);
 
 	return ( NULL );
 }
@@ -942,6 +2302,10 @@
 		PKI_STRING_free ( cid->nameHash );
 	}
 
+	if ( cid->hashAlgorithm ) {
+		X509_ALGOR_free(cid->hashAlgorithm);
+	}
+
 	PKI_Free ( cid );
 
 	return;
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/core.c openca-ocspd-3.1.2/src/ocspd/core.c
--- openca-ocspd-3.1.2-orig/src/ocspd/core.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/core.c	2025-06-19 17:33:59.065397460 +0200
@@ -10,24 +10,559 @@
 #include "general.h"
 #include "threads.h"
 #include "crl.h"
+ #include <sys/resource.h>
+#include <openssl/err.h>
+#include <poll.h>
 
 extern void auto_crl_check( int );
 extern OCSPD_CONFIG *ocspd_conf;
 
-/* Local Functions */
-void handle_sigabrt ( int i );
+static volatile sig_atomic_t hc_flag; /* set nonzero by signal handler */
+
+/* mutex internally used for the signal handling thread */
+static pthread_mutex_t sig_mutex = PTHREAD_MUTEX_INITIALIZER;
+static PKI_THREAD_ID th_id_main;
+static PKI_THREAD_ID th_id_con_hdl;
+static PKI_THREAD_ID th_id_sig_hdl;
+static PKI_THREAD *th_sig_hdl = NULL;
+static PKI_THREAD *th_con_hdl = NULL;
+
 
 /* Function Bodies */
 
-int start_threaded_server ( OCSPD_CONFIG * ocspd_conf )
+static void handle_sigusr1 ( int sig )
+{
+	int ret = PKI_OK;
+	int i;
+	int rv = 0;
+	char err_str[128];
+	int valgrind = ocspd_conf->valgrind;
+  
+
+	if(pthread_equal(PKI_THREAD_self(), th_id_main) != 0) // <> 0 means the IDs are equal
+	{
+		PKI_log_debug("Handle SIGUSR1 for main thread");
+
+		// retrieve status from signal handling thread
+		if( (rv = pthread_join(*th_sig_hdl, NULL) ) != 0)
+		{
+			PKI_strerror ( rv, err_str, sizeof(err_str));
+			PKI_log_err("pthread_join() for signal handling thread failed: [%d::%s]", rv, err_str);
+		}
+		else
+			PKI_log_debug("Signal handling thread returned");
+
+		PKI_Free(th_sig_hdl);
+
+		// retrieve status from connection handling thread
+		if( (rv = pthread_join(*th_con_hdl, NULL) ) != 0)
+		{
+			PKI_strerror ( rv, err_str, sizeof(err_str));
+			PKI_log_err("pthread_join() for connection handling thread failed: [%d::%s]", rv, err_str);
+		}
+		else
+			PKI_log_debug("Connection handling thread returned");
+
+		PKI_Free(th_con_hdl);
+
+		for (i = 0; i < ocspd_conf->nthreads; i++)
+		{
+			PKI_log_debug("Retrieve status from worker threads");
+			// retrieve status from worker threads
+			if( (rv = pthread_join(ocspd_conf->threads_list[i].thread_tid, NULL) ) != 0)
+			{
+				PKI_strerror ( rv, err_str, sizeof(err_str));
+				PKI_log_err("pthread_join() for worker thread failed: [%d::%s]", rv, err_str);
+			}
+			else
+				PKI_log(PKI_LOG_INFO, "Worker thread %d returned", i);
+		}
+
+		if(ocspd_conf) OCSPD_free_config(ocspd_conf);
+		if(ocspd_conf) PKI_Free(ocspd_conf);
+		PKI_final_all();
+		PKI_final_thread();  // also needed for the main thread
+
+		PKI_log_debug("Exiting main thread");
+		PKI_log_end();
+		if(valgrind)
+			pthread_exit((void*)&ret);
+		else
+			exit(0);
+	}
+	else if(pthread_equal(PKI_THREAD_self(), th_id_con_hdl) != 0) // <> 0 means the IDs are equal
+	{
+		PKI_log_debug("Handle SIGUSR1 for connection handling thread");
+		PKI_final_thread();
+
+		pthread_exit((void*)&ret);
+	}
+	else if(pthread_equal(PKI_THREAD_self(), th_id_sig_hdl) != 0) // <> 0 means the IDs are equal
+	{
+		PKI_log_debug("Handle SIGUSR1 for signal handling thread");
+		PKI_final_thread();
+
+		pthread_exit((void*)&ret);
+	}
+	else
+	{
+		PKI_log_debug("Handle SIGUSR1 for worker thread");
+		PKI_final_thread();
+
+		pthread_exit((void*)&ret);
+	}
+
+	return;
+}
+
+static void handle_sigusr2 ( int sig )
+{
+	switch( sig )
+	{
+		case SIGUSR2:
+
+			if(ocspd_conf->healthCheckUrl)
+			{
+				PKI_log_debug ("handle_sigusr2(): healthCheckUrl set");
+				hc_flag =~ hc_flag;
+			}
+			break;
+
+		default:
+			PKI_log_err ("handle_sigusr2(): Caught signal [%d] %s (unhandled)", sig, strsignal(sig));
+			//PKI_log (PKI_LOG_INFO, "handle_sigusr2(): Caught signal [%d] %s (unhandled)", sig, strsignal(sig));
+			break;
+	}
+
+	return;
+}
+
+static void handle_crl_reload ( int sig )
+{
+	switch( sig )
+	{
+		case SIGALRM:
+			PKI_log_debug("Handle SIGALRM for crl thread");
+			if( ocspd_conf->crl_auto_reload ||
+					ocspd_conf->crl_check_validity ) {
+				(void)auto_crl_check(sig);
+			}
+			break;
+
+		case SIGHUP:
+			PKI_log_debug("Handle SIGHUP for crl thread");
+			ocspd_reload_crls( ocspd_conf );
+			break;
+
+		/* whatever you need to do for other signals */
+		default:
+			PKI_log (PKI_LOG_INFO, "handle_crl_reload(): Caught signal [%d] %s (unhandled)", sig, strsignal(sig));
+			break;
+	}
+
+	return;
+}
+
+static void * thread_sig_handler ( void *arg )
 {
-	int i = 0;
 	int rv = 0;
+	sigset_t signal_set;
+	sigset_t orig_signal_set;
+	int sig;
+	char err_str[512];
+
+
+	th_id_sig_hdl = PKI_THREAD_self();
+
+	PKI_log_debug ( "thread_sig_handler() started");
+
+	// Register the alarm handler
+	if(set_alrm_handler() != 0)
+		return(NULL);
+
+	/* wait for any and all signals */
+	memset(&orig_signal_set, 0, sizeof(orig_signal_set));
+	if(sigfillset(&orig_signal_set) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigfillset() failed: [%d] %s", errno, err_str);
+		return(NULL);
+	}
+
+	/* ignore the following signals */
+	if(sigdelset( &orig_signal_set, SIGUSR1 ) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigdelset(SIGUSR1) failed: [%d] %s", errno, err_str);
+		return(NULL);
+	}
+	if(sigdelset( &orig_signal_set, SIGUSR2 ) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigdelset(SIGUSR2) failed: [%d] %s", errno, err_str);
+		return(NULL);
+	}
+	if(sigdelset( &orig_signal_set, SIGALRM ) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigdelset(SIGALRM) failed: [%d] %s", errno, err_str);
+		return(NULL);
+	}
+	if(sigdelset( &orig_signal_set, SIGHUP ) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigdelset(SIGHUP) failed: [%d] %s", errno, err_str);
+		return(NULL);
+	}
+
+	if( (rv = pthread_sigmask( SIG_UNBLOCK, &orig_signal_set, NULL )) == -1)
+	{
+		PKI_strerror ( rv, err_str, sizeof(err_str));
+		PKI_log_err("pthread_sigmask() failed: [%d] %s", errno, err_str);
+		return(NULL);
+	}
+
+
+	for(;;)
+	{
+		// Restore original signal set. 'signal_set' gets modified by sigwait()
+		signal_set = orig_signal_set;
+
+		sigwait( &signal_set, &sig );
+
+		/* when we get here, we've caught a signal */
+
+		PKI_log_debug("SigHandler cought signal [%d] %s \n", sig, strsignal(sig));
+
+		switch( sig )
+		{
+			case SIGQUIT:
+			case SIGTERM:
+			case SIGINT:
+			{
+				int i;
+				int err;
+
+				pthread_mutex_lock(&sig_mutex);
+				for (i = 0; i < ocspd_conf->nthreads; i++)
+				{
+					PKI_log_debug("Kill worker thread %d \n", i);
+
+					if( (err = pthread_kill(ocspd_conf->threads_list[i].thread_tid, SIGUSR1) ) != 0)
+					{
+						PKI_strerror ( err, err_str, sizeof(err_str));
+						PKI_log_err("pthread_kill() failed: [%d] %s", err, err_str);
+					}
+				}
+
+				PKI_log_debug("Kill connection handling thread\n");
+
+				if( (err = pthread_kill(th_id_con_hdl, SIGUSR1) ) != 0)
+				{
+					PKI_strerror ( err, err_str, sizeof(err_str));
+					PKI_log_err("pthread_kill() failed: [%d] %s", err, err_str);
+				}
+
+				PKI_log_debug("Kill main thread\n");
+
+				if( (err = pthread_kill(th_id_main, SIGUSR1) ) != 0)
+				{
+					PKI_strerror ( err, err_str, sizeof(err_str));
+					PKI_log_err("pthread_kill() failed: [%d] %s", err, err_str);
+				}
+				pthread_mutex_unlock(&sig_mutex);
+				err = 0;
+				PKI_final_thread();
+				pthread_exit((void*)&err);
+				break;
+			}
+
+			/* whatever you need to do for
+			 * other signals */
+			default:
+				pthread_mutex_lock(&sig_mutex);
+				PKI_log (PKI_LOG_INFO, "Caught signal [%d] %s (unhandled)", sig, strsignal(sig));
+				pthread_mutex_unlock(&sig_mutex);
+				break;
+		}
+	}
+
+	return(NULL);
+}
+
+static void cleanup_handler(void *arg)
+{
+	PKI_log_debug ( "Mutex cleanup handler called...");
+
+	PKI_MUTEX_release((PKI_MUTEX *)arg);
+}
+
+static void * thread_con_handler ( void *arg )
+{
+	int rv = 0;
+	char err_str[512];
+	sigset_t signal_set;
+	int ret = PKI_OK;
+
+
+	th_id_con_hdl = PKI_THREAD_self();
+
+	PKI_log_debug ( "thread_con_handler() started");
+
+	memset(&signal_set, 0, sizeof(signal_set));
+	if(sigemptyset(&signal_set) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigemptyset() failed: [%d] %s", errno, err_str);
+		goto exit_thread;
+	}
+
+	if(sigaddset(&signal_set, SIGUSR1) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaddset(SIGUSR1) failed: [%d] %s", errno, err_str);
+		goto exit_thread;
+	}
+
+	if(sigaddset(&signal_set, SIGUSR2) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaddset(SIGUSR2) failed: [%d] %s", errno, err_str);
+		goto exit_thread;
+	}
+
+	if( (rv = pthread_sigmask( SIG_UNBLOCK, &signal_set, NULL )) == -1)
+	{
+		PKI_strerror ( rv, err_str, sizeof(err_str));
+		PKI_log_err("pthread_sigmask() failed: [%d] %s", errno, err_str);
+		goto exit_thread;
+	}
+
+
+	for ( ; ; ) 
+	{
+		int   err = 0;
+		int   cfd = -1;
+#if 1
+		struct pollfd ufds[2];
+
+
+		if(ocspd_conf->healthCheckUrl)
+		{
+again:
+			memset(ufds, 0, sizeof(ufds));
+
+			ufds[0].events = POLLIN;
+			ufds[0].fd = ocspd_conf->listenfd;
+
+
+			if(!hc_flag && ocspd_conf->listenfd_hc == -1)
+			{
+				PKI_log(PKI_LOG_ALWAYS, "Activate health check socket\n");
+				if((ocspd_conf->listenfd_hc = PKI_NET_listen (ocspd_conf->healthCheckUrl->addr,
+							ocspd_conf->healthCheckUrl->port, PKI_NET_SOCK_STREAM )) == PKI_ERR )
+				{
+					PKI_log_err ("Can not bind to health check listener [%s:%d]",
+						ocspd_conf->healthCheckUrl->addr, ocspd_conf->healthCheckUrl->port);
+					ocspd_conf->listenfd_hc = -1;
+				}
+			}
+			else if(hc_flag && ocspd_conf->listenfd_hc != -1)
+			{
+				PKI_log(PKI_LOG_ALWAYS, "Hang up health check socket (fd %d)\n", ocspd_conf->listenfd_hc);
+				PKI_NET_close(ocspd_conf->listenfd_hc);
+				ocspd_conf->listenfd_hc = -1;
+			}
+
+			PKI_log_debug ( "Poll socket (%d, %d)", ocspd_conf->listenfd, ocspd_conf->listenfd_hc);
+
+			if(ocspd_conf->listenfd_hc > 0)
+			{
+				ufds[1].events = POLLIN;
+				ufds[1].fd = ocspd_conf->listenfd_hc;
+				rv = poll(ufds, 2, -1);
+			}
+			else
+				rv = poll(ufds, 1, -1);
+
+			switch(rv)
+			{
+				case -1:
+					if(errno != EINTR)
+					{
+						PKI_strerror ( errno, err_str, sizeof(err_str));
+						PKI_log_err("Network Error [%d::%s]", err, err_str);
+					}
+					else // EINTR
+					{
+						PKI_log_debug ( "poll() call interrupted..");
+						goto again;
+					}
+					err = 1;
+					break;
+				case 0:
+					err = 1;
+					break;
+				default:
+					break;
+			}
+
+			if(!err)
+			{
+				if(ufds[0].revents & POLLIN)
+				{
+					cfd = ocspd_conf->listenfd;
+				}
+				else if(ufds[1].revents & POLLIN)
+				{
+					cfd = ocspd_conf->listenfd_hc;
+				}
+			}
+		}
+#else
+		fd_set  readfds;
+		fd_set  except;
+
+
+		if(ocspd_conf->healthCheckUrl)
+		{
+
+again:
+			FD_ZERO(&readfds);
+			FD_ZERO(&except);
+
+			FD_SET(ocspd_conf->listenfd, &readfds);
+			FD_SET(ocspd_conf->listenfd, &except);
+
+			if(!hc_flag && ocspd_conf->listenfd_hc == -1)
+			{
+				PKI_log(PKI_LOG_ALWAYS, "Activate health check socket\n");
+				if((ocspd_conf->listenfd_hc = PKI_NET_listen (ocspd_conf->healthCheckUrl->addr,
+							ocspd_conf->healthCheckUrl->port, PKI_NET_SOCK_STREAM )) == PKI_ERR )
+				{
+					PKI_log_err ("Can not bind to health check listener [%s:%d]",
+						ocspd_conf->healthCheckUrl->addr, ocspd_conf->healthCheckUrl->port);
+					ocspd_conf->listenfd_hc = -1;
+				}
+			}
+			else if(hc_flag && ocspd_conf->listenfd_hc != -1)
+			{
+				PKI_log(PKI_LOG_ALWAYS, "Hang up health check socket (fd %d)\n", ocspd_conf->listenfd_hc);
+				PKI_NET_close(ocspd_conf->listenfd_hc);
+				ocspd_conf->listenfd_hc = -1;
+			}
+
+			if(ocspd_conf->listenfd_hc > 0)
+			{
+				FD_SET(ocspd_conf->listenfd_hc, &readfds);
+				FD_SET(ocspd_conf->listenfd_hc, &except);
+			}
+
+			PKI_log_debug ( "Select socket (%d, %d)", ocspd_conf->listenfd, ocspd_conf->listenfd_hc);
+
+			if( (rv = select(FD_SETSIZE, &readfds, NULL, &except, NULL)) < 0)
+			{
+				if(errno != EINTR)
+				{
+					PKI_strerror ( errno, err_str, sizeof(err_str));
+					PKI_log_err("Network Error [%d::%s]", err, err_str);
+				}
+				else // EINTR
+				{
+					PKI_log_debug ( "select() call interrupted..");
+					goto again;
+				}
+				err = 1;
+			}
+			else if(rv == 0) // Timeout?
+			{
+				err = 1;
+			}
+			else if(FD_ISSET(ocspd_conf->listenfd, &except) ||
+						FD_ISSET(ocspd_conf->listenfd_hc, &except)) // any exception available?
+			{
+				PKI_log_err("Socket exception occured!\n");
+				err = 1;
+			}
+
+			if(!err)
+			{
+				if(FD_ISSET(ocspd_conf->listenfd, &readfds))
+					cfd = ocspd_conf->listenfd;
+				else if(FD_ISSET(ocspd_conf->listenfd_hc, &readfds))
+					cfd = ocspd_conf->listenfd_hc;
+			}
+		}
+#endif
+		else
+			cfd = ocspd_conf->listenfd;
+
+		// Acquires the Mutex for handling the ocspd_conf->connfd
+		pthread_cleanup_push(cleanup_handler, &ocspd_conf->mutexes[CLIFD_MUTEX]);
+		PKI_MUTEX_acquire ( &ocspd_conf->mutexes[CLIFD_MUTEX] );
+		PKI_log_debug ( "Con thread: Wait for a new connection..");
+
+		if( (ocspd_conf->connfd = PKI_NET_accept(cfd, 0) ) == -1)
+		{
+			// Provides some information about the error
+			if (ocspd_conf->verbose || ocspd_conf->debug)
+			{
+				char err_str[512];
+				PKI_strerror ( errno, err_str, sizeof(err_str));
+				PKI_log_err("Network Error [%d::%s]", err, err_str);
+			}
+
+			err = 1;
+		}
+		else
+		{
+			// Communicate that there is a good socket waiting for a thread to pickup
+			PKI_COND_broadcast ( &ocspd_conf->condVars[CLIFD_COND] );
+		}
+
+		// Release the connfd MUTEX
+		PKI_log_debug ( "worker thread: release mutex..");
+		pthread_cleanup_pop(1);
+
+		if(err)
+		{
+			// Restart from the top of the cycle
+			continue;
+		}
 
-	struct sockaddr_in cliaddr;
-	socklen_t cliaddrlen;
+		PKI_log_debug ( "Con thread: got new connection..");
 
+		// Waits for a thread to successfully pickup the socket
+		PKI_log_debug ( "Con thread: acquire next mutex..");
+		pthread_cleanup_push(cleanup_handler, &ocspd_conf->mutexes[SRVFD_MUTEX]);
+		PKI_MUTEX_acquire ( &ocspd_conf->mutexes[SRVFD_MUTEX] );
+		while (ocspd_conf->connfd > 2)
+		{
+			PKI_log_debug ( "Con thread: cond wait..");
+			PKI_COND_wait ( &ocspd_conf->condVars[SRVFD_COND],
+				&ocspd_conf->mutexes[SRVFD_MUTEX] );
+		}
+		pthread_cleanup_pop(1);
+		PKI_log_debug ( "Con thread: connection handled by a thread");
+	}
+
+
+exit_thread:
+	PKI_final_thread();
+	pthread_exit((void*)&ret);
+
+	return(NULL);
+}
+
+int start_threaded_server ( OCSPD_CONFIG * ocspd_conf )
+{
+	int i = 0;
+	int rv = 0;
+	sigset_t signal_set;
 	struct sigaction sa;
+	char err_str[512];
+
+	th_id_main = PKI_THREAD_self();
 
 	// Just print a nice log message when exits
 	atexit(close_server);
@@ -38,17 +573,18 @@
 				ocspd_conf->token_config_dir, ocspd_conf->token_name)
 								== PKI_ERR)
 		{
-			PKI_log_err( "Can not load default token (%s/%s)",
+			PKI_log_err( "Can not load default token (%s:%s)",
 				ocspd_conf->cnf_filename, ocspd_conf->token_name );
-			exit(1);
+			return(1);
 		}
 
+
 		PKI_TOKEN_cred_set_cb ( ocspd_conf->token, NULL, NULL);
 
 		if (PKI_TOKEN_login ( ocspd_conf->token ) != PKI_OK)
 		{
 			PKI_log_debug("Can not login into token!");
-			exit(1);
+			return(1);
 		}
 
 		rv = PKI_TOKEN_check(ocspd_conf->token);
@@ -61,7 +597,7 @@
 			if (rv & PKI_TOKEN_STATUS_CACERT_ERR) PKI_ERROR(PKI_ERR_TOKEN_CACERT_LOAD, NULL);
 
 			PKI_log_err("Token Configuration Fatal Error (%d)", rv);
-			exit(rv);
+			return(rv);
 		}
 	}
 
@@ -76,28 +612,22 @@
 		if (ca->token_name == NULL)
 			continue;
 
-		if ((ca->token = PKI_TOKEN_new_null()) == NULL)
-		{
-			PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL);
-			exit (1);
-		}
-
-		PKI_TOKEN_cred_set_cb ( ocspd_conf->token, NULL, NULL);
-
 		rv = PKI_TOKEN_init(ca->token, ca->token_config_dir, ca->token_name);
 		if (rv != PKI_OK)
 		{
 			PKI_ERROR(rv, NULL);
 			PKI_log_err ( "Can not load token %s for CA %s (%s)",
 				ca->token_name, ca->ca_id, ca->token_config_dir );
-			exit (rv);
+			return (rv);
 		}
 
+		PKI_TOKEN_cred_set_cb ( ca->token, NULL, NULL);
+
 		rv = PKI_TOKEN_login(ca->token);
 		if (rv != PKI_OK)
 		{
 			PKI_log_err("Can not login into token (%s)!", ca->ca_id);
-			exit(rv);
+			return(rv);
 		}
 
 		rv = PKI_TOKEN_check(ca->token);
@@ -105,12 +635,12 @@
 							 PKI_TOKEN_STATUS_CERT_ERR |
 							 PKI_TOKEN_STATUS_CACERT_ERR))
 		{
-			if (rv & PKI_TOKEN_STATUS_KEYPAIR_ERR) PKI_ERROR(PKI_TOKEN_STATUS_KEYPAIR_ERR, NULL);
-			if (rv & PKI_TOKEN_STATUS_CERT_ERR) PKI_ERROR(PKI_TOKEN_STATUS_CERT_ERR, NULL);
-			if (rv & PKI_TOKEN_STATUS_CACERT_ERR) PKI_ERROR(PKI_TOKEN_STATUS_CACERT_ERR, NULL);
+			if (rv & PKI_TOKEN_STATUS_KEYPAIR_ERR) PKI_ERROR(PKI_ERR_TOKEN_KEYPAIR_LOAD, NULL);
+			if (rv & PKI_TOKEN_STATUS_CERT_ERR) PKI_ERROR(PKI_ERR_TOKEN_CERT_LOAD, NULL);
+			if (rv & PKI_TOKEN_STATUS_CACERT_ERR) PKI_ERROR(PKI_ERR_TOKEN_CACERT_LOAD, NULL);
 
 			PKI_log_err ( "Token Configuration Fatal Error (%d) for ca %s", rv, ca->ca_id);
-			exit(rv);
+			return(rv);
 		}
 	}
 
@@ -118,14 +648,25 @@
 					ocspd_conf->bindUrl->port, PKI_NET_SOCK_STREAM )) == PKI_ERR ) {
 		PKI_log_err ("Can not bind to [%s],[%d]",
 			ocspd_conf->bindUrl->addr, ocspd_conf->bindUrl->port);
-		exit(101);
+		return(101);
+	}
+
+	if(ocspd_conf->healthCheckUrl) {
+		PKI_log(PKI_LOG_ALWAYS, "Activate health check socket\n");
+		if((ocspd_conf->listenfd_hc = PKI_NET_listen (ocspd_conf->healthCheckUrl->addr,
+					ocspd_conf->healthCheckUrl->port, PKI_NET_SOCK_STREAM )) == PKI_ERR )
+		{
+			PKI_log_err ("Can not bind to health check listener [%s:%d]",
+				ocspd_conf->healthCheckUrl->addr, ocspd_conf->healthCheckUrl->port);
+			return(102);
+		}
 	}
 
 	// Now Chroot the application
 	if ((ocspd_conf->chroot_dir) && (set_chroot( ocspd_conf ) < 1))
 	{
 		PKI_log_err ("Can not chroot, exiting!");
-		exit(204);
+		return(204);
 	}
 
 	// Set privileges
@@ -140,92 +681,189 @@
 		{
 			PKI_log(PKI_LOG_ALWAYS, "SECURITY:: Can not drop privileges! [204]");
 			PKI_log(PKI_LOG_ALWAYS, "SECURITY:: Check User/Group in config file!");
-			exit(204);
+			return(204);
+		}
+	}
+
+	if(ocspd_conf->daemon)
+	{
+		int fd; 
+
+		// loose control terminal and become a session group leader
+		if(setsid() < 0)
+		{
+			PKI_log_err ("setsid() failed: [%d] %s", errno, strerror(errno) );
+			return(1);
+		}
+
+		// close stdin, stdout and stderr
+		close(0);
+		close(1);
+		close(2);
+
+
+		// redirect stdin, stdout and stderr to /dev/null
+		if( (fd = open("/dev/null", O_RDWR, 0) ) < 0)
+		{
+			PKI_log_err ("open(/dev/null) failed: [%d] %s", errno, strerror(errno) );
+			return(1);
+		}
+
+		if(dup2(fd, STDIN_FILENO) < 0)
+		{
+			PKI_log_err ("dup2(stdin) failed: [%d] %s", errno, strerror(errno) );
+			return(1);
+		}
+		if(dup2(fd, STDOUT_FILENO) < 0)
+		{
+			PKI_log_err ("dup2(stdout) failed: [%d] %s", errno, strerror(errno) );
+			return(1);
 		}
+		if(dup2(fd, STDERR_FILENO) < 0)
+		{
+			PKI_log_err ("dup2(stderr) failed: [%d] %s", errno, strerror(errno) );
+			return(1);
+		}
+
+		if(fd > 2)
+			close (fd);  
+	}
+
+	/* We set our needed signal handlers in the main program (aka main thread).
+	 * Later we will create some sub-threads as worker threads, connection handler
+	 * and signal handler. Depending on the thread some signals will then be
+	 * blocked using pthread_sigmask() */
+
+	/* Using SIGUSR1 for a clean termination of the threads */
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = handle_sigusr1;
+	sigemptyset(&sa.sa_mask);
+	
+	if (sigaction(SIGUSR1, &sa, NULL) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaction(SIGUSR1) failed [%d::%s]", errno, err_str);
+		return(1);
+	}
+
+	/* Using SIGUSR2 to toggle the health check port and 
+	 * to inform the connection handling thread */
+	sa.sa_handler = handle_sigusr2;
+	sigemptyset(&sa.sa_mask);
+	
+	if (sigaction(SIGUSR2, &sa, NULL) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaction(SIGUSR2) failed [%d::%s]", errno, err_str);
+		return(1);
+	}
+
+
+	/* A CRL reload is performed on SIGALRM and SIGHUP */
+	sa.sa_handler = handle_crl_reload;
+
+	if (sigaction(SIGALRM, &sa, NULL) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaction(SIGALRM) failed [%d::%s]", errno, err_str);
+		return(1);
+	}
+
+	if (sigaction(SIGHUP, &sa, NULL) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaction(SIGHUP) failed [%d::%s]", errno, err_str);
+		return(1);
+	}
+
+	/* block all signals in the main thread */
+	memset(&signal_set, 0, sizeof(signal_set));
+	if(sigfillset( &signal_set ) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigfillset() failed: [%d] %s", errno, err_str);
+		return(1);
+	}
+
+	if( (rv = pthread_sigmask( SIG_BLOCK, &signal_set, NULL )) == -1)
+	{
+		PKI_strerror ( rv, err_str, sizeof(err_str));
+		PKI_log_err("pthread_sigmask() failed: [%d] %s", rv, err_str);
+		return(1);
+	}
+
+	/* create the signal handling thread (only ignores SIGALRM, SIGHUP and SIGUSR1+2) */
+	if ((th_sig_hdl = PKI_THREAD_new(thread_sig_handler, NULL)) == NULL)
+	{
+		PKI_log_err("ERROR::OPENCA_SRV_ERR_THREAD_CREATE");
+		return(-1);
 	}
 
 	if((ocspd_conf->threads_list = calloc ( (size_t) ocspd_conf->nthreads, 
 					sizeof(Thread))) == NULL )
 	{
 		PKI_log_err ("Memory allocation failed");
-		exit(79);
+		return(79);
 	}
 
-	// Creates the Threads
+	/* Create the worker threads */
 	for (i = 0; i < ocspd_conf->nthreads; i++)
 	{
 		if (thread_make(i) != 0)
 		{
 			PKI_log_err ("Can not create thread (%d)\n", i );
-			exit(80);
+			return(80);
 		}
 	}
 
-	// Register the alarm handler
-	set_alrm_handler();
+	/* create the connection handling thread */
+	if ((th_con_hdl = PKI_THREAD_new(thread_con_handler, NULL)) == NULL)
+	{
+		PKI_log_err("ERROR::OPENCA_SRV_ERR_THREAD_CREATE");
+		return(-1);
+	}
 
-	// Just print a nice log message when killed
-	signal(SIGTERM, handle_sigterm );
-	signal(SIGABRT, handle_sigabrt );
-
-	// Setting the SIGHUP in order to reload the CRLs
-	// sa.sa_handler = auto_crl_check;
-	sa.sa_handler = force_crl_reload;
-	sigemptyset(&sa.sa_mask);
-	sa.sa_flags = SA_RESTART;
+	PKI_log(PKI_LOG_ALWAYS, "Started connection handling thread - ready for accepting OCSP requests.");
 
-	if (sigaction(SIGHUP, &sa, NULL) == -1)
+	memset(&signal_set, 0, sizeof(signal_set));
+	if(sigemptyset(&signal_set) == -1)
 	{
-		PKI_log_err("Error during setting sig_handler");
-		exit(1);
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigemptyset() failed: [%d] %s", errno, err_str);
+		return(-1);
 	}
 
-	cliaddrlen = sizeof( cliaddr );
-	for ( ; ; ) 
+	if(sigaddset(&signal_set, SIGUSR1) == -1)
 	{
-		// Acquires the Mutex for handling the ocspd_conf->connfd
-		PKI_MUTEX_acquire ( &ocspd_conf->mutexes[CLIFD_MUTEX] );
-		if ((ocspd_conf->connfd = PKI_NET_accept(ocspd_conf->listenfd, 0)) == -1)
-		{
-			// Provides some information about the error
-			if (ocspd_conf->verbose || ocspd_conf->debug)
-			{
-				char err_str[512];
-				PKI_log_err("Network Error [%d::%s]", errno,
-						strerror_r(errno, err_str, sizeof(err_str)));
-			}
-
-			// Returns the connfd MUTEX and restart from the top of the cycle
-			PKI_MUTEX_release(&ocspd_conf->mutexes[CLIFD_MUTEX]);
-			continue;
-		}
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaddset(SIGUSR1) failed: [%d] %s", errno, err_str);
+		return(-1);
+	}
 
-		// Some debugging information
-		if (ocspd_conf->debug)
-		{
-			if (getpeername(ocspd_conf->connfd, (struct sockaddr*)&cliaddr, &cliaddrlen) == -1)
-			{
-				char err_str[512];
-				PKI_log_err("Network Error [%d::%s] in getpeername", errno,
-					strerror_r(errno, err_str, sizeof(err_str)));
-			}
+	if(sigaddset(&signal_set, SIGHUP) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaddset(SIGHUP) failed: [%d] %s", errno, err_str);
+		return(-1);
+	}
 
-			PKI_log(PKI_LOG_INFO, "Connection from [%s]",
-	 			inet_ntoa(cliaddr.sin_addr));
-		}
+	if(sigaddset(&signal_set, SIGALRM) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaddset(SIGALRM) failed: [%d] %s", errno, err_str);
+		return(-1);
+	}
 
-		// Communicate that there is a good socket waiting for a thread to pickup
-		PKI_COND_broadcast ( &ocspd_conf->condVars[CLIFD_COND] );
-		PKI_MUTEX_release ( &ocspd_conf->mutexes[CLIFD_MUTEX] );
+	if( (rv = pthread_sigmask( SIG_UNBLOCK, &signal_set, NULL )) == -1)
+	{
+		PKI_strerror ( rv, err_str, sizeof(err_str));
+		PKI_log_err("pthread_sigmask() failed: [%d] %s", errno, err_str);
+		return(-1);
+	}
 
-		// Waits for a thread to successfully pickup the socket
-		PKI_MUTEX_acquire ( &ocspd_conf->mutexes[SRVFD_MUTEX] );
-		while (ocspd_conf->connfd > 2)
-		{
-			PKI_COND_wait ( &ocspd_conf->condVars[SRVFD_COND],
-				&ocspd_conf->mutexes[SRVFD_MUTEX] );
-		}
-		PKI_MUTEX_release ( &ocspd_conf->mutexes[SRVFD_MUTEX] );
+	while(1)
+	{
+		pause();
 	}
 
 	return(0);
@@ -235,7 +873,6 @@
 
 	/* Now on the parent process we setup the auto_checking
 	   functions */
-	struct sigaction sa;
 
 	if( ocspd_conf->crl_auto_reload ||
 			ocspd_conf->crl_check_validity ) {
@@ -253,46 +890,10 @@
 				(val_check ? val_check : auto_rel) : 
 					(auto_rel ? auto_rel : val_check ));
 
-		sa.sa_handler = auto_crl_check;
-		sigemptyset(&sa.sa_mask);
-		sa.sa_flags = SA_RESTART;
-
-		if (sigaction(SIGALRM, &sa, NULL) == -1) {
-			PKI_log_err("Error handling the death processes");
-			exit(1);
-		}
-
-	 	/* signal( SIGALRM, auto_crl_check ); */
-	 	alarm ( (unsigned int) ocspd_conf->alarm_decrement );
-	} else {
-		signal( SIGALRM, SIG_IGN);
+		alarm ( (unsigned int) ocspd_conf->alarm_decrement );
 	}
 
-	return 1;
-}
-
-void handle_sighup ( int i ) {
-
-	PKI_log( PKI_LOG_WARNING, "SIGHUP::Reloading CRLs, Master!");
-	ocspd_reload_crls( ocspd_conf );
-	return;
-}
-
-void handle_sigterm ( int i ) {
-	if( ocspd_conf->verbose ) {
-		PKI_log (PKI_LOG_INFO,"SIGTERM::Received TERM signal");
-	}
-	exit(0);
-	return;
-}
-
-void handle_sigabrt ( int i ) {
-
-	PKI_log_err("SIGABRT::received - should not happen,");
-	PKI_log_err("SIGABRT::please enable strict locking.");
-	PKI_log_err("ERROR::SIGABRT::Fatal Error, aborting server!");
-
-	return;
+	return 0;
 }
 
 void close_server ( void ) {
@@ -305,6 +906,9 @@
 	struct passwd *pw = NULL;
 	struct group *gr = NULL;
 
+	if( !conf->group || !conf->user)
+		return 1;
+
 	if( (gr = getgrnam( conf->group ) ) == NULL ) {
 		PKI_log_err("Cannot find group %s", conf->group);
 		return 0;
@@ -315,15 +919,24 @@
 		return 0;
 	}
 
+	/* When dropping privileges from root, the setgroups() call will
+	 * remove any extraneous groups.
+	 */
+
+	if(setgroups(0, NULL) == -1) {
+		PKI_log_err ("Dropping privileges failed [%s]", strerror(errno));
+		return 0;
+	}
+
 	if (setgid (gr->gr_gid) == -1) {
-		PKI_log_err ("Error setting group %d (%s)", 
-			gr->gr_gid, conf->group);
+		PKI_log_err ("Error setting group %d (%s): %s", 
+			gr->gr_gid, conf->group, strerror(errno));
 		return 0;
 	}
 
 	if (setuid (pw->pw_uid) == -1) {
-		PKI_log_err("Error setting user %d (%s)", 
-						pw->pw_uid, conf->user );
+		PKI_log_err("Error setting user %d (%s): %s", 
+						pw->pw_uid, conf->user, strerror(errno));
 		return 0;
 	}
 
@@ -356,3 +969,4 @@
 	/* Ok, chdir and chroot! */
 	return(1);
 }
+
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/crl.c openca-ocspd-3.1.2/src/ocspd/crl.c
--- openca-ocspd-3.1.2-orig/src/ocspd/crl.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/crl.c	2025-08-21 16:14:13.039403562 +0200
@@ -25,71 +25,105 @@
 
 int ocspd_load_ca_crl ( CA_LIST_ENTRY *a, OCSPD_CONFIG *conf ) {
 
+	struct stat st;
+	int crl_status = 0;
+	CRL_DATA *crl_data = NULL;
+	CRL_DATA *p_crl_data = NULL;
+	char err_str[128];
+
 	if(!a) return(-1);
 
-	if( conf->debug )
-		PKI_log_debug( "ACQUIRING WRITE LOCK -- BEGIN CRL RELOAD");
+	if( a->crl_url == NULL ) {
+		PKI_log_err ( "Missing CRL URL for CA %s", a->ca_id );
+		return(-1);
+	}
+
+	memset(&st, 0, sizeof(st));
 
-	PKI_RWLOCK_write_lock ( &conf->crl_lock );
-	// pthread_rwlock_wrlock( &crl_lock );
-	if( conf->debug )
-		PKI_log_debug( "INFO::LOCK ACQUIRED (CRL RELOAD)");
+	PKI_RWLOCK_read_lock ( &a->single_crl_lock );
 
-	if( a->crl ) PKI_X509_CRL_free ( a->crl );
+	if(conf->crl_check_mtime) {
+		if(stat(a->crl_url->addr, &st) == -1) {
+			PKI_strerror ( errno, err_str, sizeof(err_str));
+			PKI_log_err ("Cannot access CRL for CA [ %s ] [%d::%s]. Skipping mtime check.", 
+				a->ca_id, errno, err_str );
+		}
+		else if(st.st_mtime <= a->crl_data->mtime) {
+			if( conf->verbose ) {
+				PKI_log( PKI_LOG_INFO, "INFO::CRL for CA [ %s ] was not updated. Skipping reload.",
+			a->ca_id);
+			}
 
-	a->crl = NULL;
-	a->crl_list = NULL;
+			PKI_RWLOCK_release_read ( &a->single_crl_lock );
 
-	if( a->crl_url == NULL ) {
-		 PKI_log_err ( "Missing CRL URL for CA %s", a->ca_id );
+			/* Check the validity of the previously loaded CRL to update it's status */
+			crl_status = check_crl_validity( a->crl_data, &a->single_crl_lock, conf, a->ca_id );
+
+			PKI_RWLOCK_write_lock ( &a->single_crl_lock );
+			a->crl_data->crl_status = crl_status;
+			PKI_RWLOCK_release_write ( &a->single_crl_lock );
+
+			return(0);
+		}
+	}
+
+	PKI_RWLOCK_release_read ( &a->single_crl_lock );
+  
+	if( (crl_data = PKI_Malloc(sizeof(CRL_DATA)) ) == NULL)
+	{
+		PKI_log_err ("PKI_Malloc(CRL_DATA) failed (%d bytes)\n", sizeof(CRL_DATA));
 		return(-1);
 	}
 
 	/* We now re-load the CRL */
-	if( (a->crl = PKI_X509_CRL_get_url( a->crl_url, NULL, NULL)) == NULL ) {
-		PKI_log_err ("Can not reload CRL [ %s ] for CA [%s]", 
-						a->crl_url->addr, a->ca_id);
-		PKI_RWLOCK_release_write ( &conf->crl_lock );
+	if( (crl_data->crl = PKI_X509_CRL_get_url( a->crl_url, NULL, NULL)) == NULL ) {
+		PKI_log_err ("Can not reload CRL [ %s ] for CA [%s]", a->crl_url->addr, a->ca_id);
 		return(-1);
 	}
 
 	if( conf->verbose )
-		PKI_log( PKI_LOG_INFO, "INFO::CRL successfully reloaded [ %s ]",
-			a->ca_id );
+		PKI_log( PKI_LOG_INFO, "INFO::CRL successfully reloaded [ %s ]", a->ca_id );
 
 	/* Let's get the CRLs entries, if any */
-	if( ocspd_build_crl_entries_list ( a, a->crl ) == NULL ) { 
+	if( (crl_data->crl_list = ocspd_build_crl_entries_list ( crl_data->crl, a->ca_id ) ) == NULL ) { 
 		if( conf->verbose )
-			PKI_log(PKI_LOG_INFO, "INFO::No Entries for CRL [ %s ]",
-				a->ca_id );
+			PKI_log(PKI_LOG_INFO, "INFO::No Entries for CRL [ %s ]", a->ca_id );
 	};
 
 	if(conf->verbose)
-		PKI_log( PKI_LOG_INFO, "INFO::CRL loaded successfully [ %s ]", 
-								a->ca_id );
-
-	/* If previous values are there, then we clear them up */
-	if ( a->lastUpdate ) ASN1_TIME_free(a->lastUpdate);
-	if ( a->nextUpdate ) ASN1_TIME_free(a->nextUpdate);
+		PKI_log( PKI_LOG_INFO, "INFO::CRL loaded successfully [ %s ]", a->ca_id );
 
 	/* Get new values from the recently loaded CRL */
-	a->lastUpdate = M_ASN1_TIME_dup (
-		PKI_X509_CRL_get_data ( a->crl, PKI_X509_DATA_LASTUPDATE ));
-	a->nextUpdate = M_ASN1_TIME_dup (
-		PKI_X509_CRL_get_data ( a->crl, PKI_X509_DATA_NEXTUPDATE ));
-
-	if(conf->debug) PKI_log_debug("RELEASING LOCK (CRL RELOAD)");
-	PKI_RWLOCK_release_write ( &conf->crl_lock );
-	// pthread_rwlock_unlock ( &crl_lock );
-	if(conf->debug) PKI_log_debug ( "LOCK RELEASED --END--");
+	crl_data->lastUpdate = M_ASN1_TIME_dup (
+		PKI_X509_CRL_get_data ( crl_data->crl, PKI_X509_DATA_LASTUPDATE ));
+	crl_data->nextUpdate = M_ASN1_TIME_dup (
+		PKI_X509_CRL_get_data ( crl_data->crl, PKI_X509_DATA_NEXTUPDATE ));
 
 	/* Now check the CRL validity */
-	a->crl_status = check_crl_validity( a, conf );
+	crl_data->crl_status = check_crl_validity( crl_data, NULL, conf, a->ca_id );
+
+	if( crl_data->crl_status == CRL_OK ) {
+		PKI_log(PKI_LOG_ALWAYS, "%s's CRL reloaded (OK, %d entries)", a->ca_id, sk_X509_REVOKED_num(crl_data->crl_list));
+	}
 
-	if( a->crl_status == CRL_OK ) {
-		PKI_log(PKI_LOG_ALWAYS, "%s's CRL reloaded (OK)", a->ca_id);
+	if(conf->crl_check_mtime) {
+		/* Update mtime */
+		crl_data->mtime = st.st_mtime;
 	}
 
+	//if(conf->debug) PKI_log_debug( "ocspd_load_ca_crl(): ACQUIRING WRITE LOCK");
+	PKI_RWLOCK_write_lock ( &a->single_crl_lock );
+	//if(conf->debug) PKI_log_debug( "ocspd_load_ca_crl(): LOCK ACQUIRED");
+
+	p_crl_data = a->crl_data;
+	a->crl_data = crl_data;
+
+	//if(conf->debug) PKI_log_debug("ocspd_load_ca_crl(): RELEASING LOCK (CRL RELOAD)");
+	PKI_RWLOCK_release_write ( &a->single_crl_lock );
+	//if(conf->debug) PKI_log_debug ( "ocspd_load_ca_crl(): LOCK RELEASED --END--");
+
+	CRL_DATA_free_all(p_crl_data);
+  
 	return(0);
 }
 
@@ -108,6 +142,13 @@
 	for( i=0; i < PKI_STACK_elements (conf->ca_list); i++ ) {
 		a = PKI_STACK_get_num ( conf->ca_list, i );
 
+		if(a->revocation_check && !a->crl_fallback) {
+			if( conf->verbose )
+				PKI_log(PKI_LOG_INFO, "INFO::No CRL configured for CA [%s] - skipping reload",
+							a->ca_id );
+			continue;
+		}
+
 		if( conf->verbose )
 			PKI_log(PKI_LOG_INFO, "INFO::Reloading CRL for CA [%s]",
 							a->ca_id );
@@ -125,7 +166,7 @@
 	return(1);
 }
 
-int check_crl ( PKI_X509_CRL *x_crl, PKI_X509_CERT *x_cacert,
+int check_crl ( PKI_X509_CRL *x_crl, PKI_X509_CERT *x_cacert, PKI_RWLOCK *single_crl_lock,
 		OCSPD_CONFIG *conf ) {
 
 	PKI_X509_KEYPAIR_VALUE *pkey = NULL;
@@ -135,7 +176,7 @@
 
 	if (!conf) return (-1);
 
-	PKI_RWLOCK_read_lock ( &conf->crl_lock );
+	PKI_RWLOCK_read_lock ( single_crl_lock );
 	if( !x_crl || !x_crl->value || !x_cacert || !x_cacert->value ) {
 		if( conf->verbose ) {
 			if(!x_crl || !x_crl->value) 
@@ -143,7 +184,7 @@
 			if(!x_cacert || !x_cacert->value) 
 					PKI_log_err("CA cert missing");
 		}
-		PKI_RWLOCK_release_read ( &conf->crl_lock );
+		PKI_RWLOCK_release_read ( single_crl_lock );
 		return(-1);
 	}
 
@@ -151,14 +192,15 @@
 	if((pkey = PKI_X509_CERT_get_data( x_cacert, 
 				PKI_X509_DATA_PUBKEY )) == NULL ) { 
 		PKI_log_err( "Can not parse PubKey from CA Cert");
-		PKI_RWLOCK_release_read ( &conf->crl_lock );
+		PKI_RWLOCK_release_read ( single_crl_lock );
 		return(-3);
 	}
 
 	if ((k = PKI_X509_new_value(PKI_DATATYPE_X509_KEYPAIR, pkey, NULL))
 							== NULL ) {
 		PKI_log_err ("Memory Error!");
-		PKI_RWLOCK_release_read ( &conf->crl_lock );
+		PKI_RWLOCK_release_read ( single_crl_lock );
+		EVP_PKEY_free (pkey);
 		return(-3);
 	}
 	
@@ -171,8 +213,9 @@
 
 	k->value = NULL;
 	PKI_X509_KEYPAIR_free ( k );
+	EVP_PKEY_free (pkey);
 
-	PKI_RWLOCK_release_read ( &conf->crl_lock );
+	PKI_RWLOCK_release_read ( single_crl_lock );
 
 	if ( ret > 0 ) {
 		PKI_log(PKI_LOG_INFO, "CRL matching CA cert ok [ %d ]",
@@ -182,65 +225,53 @@
 	return ret;
 }
 
-int check_crl_validity ( CA_LIST_ENTRY *ca, OCSPD_CONFIG *conf ) {
+int check_crl_validity ( CRL_DATA *crl_data, PKI_RWLOCK *lock, OCSPD_CONFIG *conf, char *ca_id ) {
 	int i;
 
-	PKI_RWLOCK_read_lock ( &conf->crl_lock );
-	// pthread_rwlock_rdlock( &crl_lock );
+	if(lock) PKI_RWLOCK_read_lock ( lock );
 
-	if( (!ca) || (!ca->crl) || (!(ca->lastUpdate)) ) {
-		PKI_log_err ("CRL::[%s]::Verify error (memory alloc)", 
-								ca->ca_id );
-		PKI_RWLOCK_release_read ( &conf->crl_lock );
-		// pthread_rwlock_unlock( &crl_lock );
+	if( (!crl_data) || (!crl_data->crl) || (!(crl_data->lastUpdate)) ) {
+		PKI_log_err ("CRL::[%s]::Input parameter error", ca_id );
+		if(lock) PKI_RWLOCK_release_read ( lock );
 		return(CRL_ERROR_LAST_UPDATE);
 	}
 
-	i=X509_cmp_time(ca->lastUpdate, NULL);
+	i=X509_cmp_time(crl_data->lastUpdate, NULL);
 	if (i == 0) {
 		PKI_log_err( "CRL [%s] LAST UPDATE error (code %d)", 
-			ca->ca_id, CRL_ERROR_LAST_UPDATE );
+			ca_id, CRL_ERROR_LAST_UPDATE );
 
-		PKI_RWLOCK_release_read ( &conf->crl_lock );
-		ca->crl_status = CRL_ERROR_LAST_UPDATE;
-		// pthread_rwlock_unlock( &crl_lock );
+		if(lock) PKI_RWLOCK_release_read ( lock );
 		return(CRL_ERROR_LAST_UPDATE);
 	} else if (i > 0) {
 		PKI_log_err("WARING::CRL [%s] NOT YET valid (code %d)", 
-				ca->ca_id, CRL_NOT_YET_VALID);
-		ca->crl_status = CRL_NOT_YET_VALID;
-		PKI_RWLOCK_release_read ( &conf->crl_lock );
-		// pthread_rwlock_unlock( &crl_lock );
+				ca_id, CRL_NOT_YET_VALID);
+
+		if(lock) PKI_RWLOCK_release_read ( lock );
 		return(CRL_NOT_YET_VALID);
 	}
                                                                                 
 	if(!conf->crl_reload_expired) {
-		if(ca->nextUpdate) {
-			i=X509_cmp_time(ca->nextUpdate, NULL);
+		if(crl_data->nextUpdate) {
+			i=X509_cmp_time(crl_data->nextUpdate, NULL);
                                                                                   
 			if (i == 0) {
-				PKI_RWLOCK_release_read ( &conf->crl_lock );
-				// pthread_rwlock_unlock( &crl_lock );
+				if(lock) PKI_RWLOCK_release_read ( lock );
 				PKI_log_err ("CRL [%s] NEXT UPDATE error (code %d)", 
-						ca->ca_id, CRL_ERROR_NEXT_UPDATE );
-				ca->crl_status = CRL_ERROR_NEXT_UPDATE;
-				// pthread_rwlock_unlock( &crl_lock );
+						ca_id, CRL_ERROR_NEXT_UPDATE );
 				return(CRL_ERROR_NEXT_UPDATE);
 			} else if (i < 0) {
-				PKI_RWLOCK_release_read ( &conf->crl_lock );
-				// pthread_rwlock_unlock( &crl_lock );
+				if(lock) PKI_RWLOCK_release_read ( lock );
 				PKI_log_err ("CRL [%s] IS EXPIRED (code %d)",
-						ca->ca_id, CRL_EXPIRED );
-				ca->crl_status = CRL_EXPIRED;
+						ca_id, CRL_EXPIRED );
 				return(CRL_EXPIRED);
 			}
 		} else {
-			PKI_log_err ("CRL [%s] has no nextUpdate!", ca->ca_id );
+			PKI_log_err ("CRL [%s] has no nextUpdate!", ca_id );
 		}
 	}
 
-	PKI_RWLOCK_release_read ( &conf->crl_lock );
-	// pthread_rwlock_unlock( &crl_lock );
+	if(lock) PKI_RWLOCK_release_read ( lock );
 
 	PKI_log_debug("CRL::Verify %d [OK=%d]", i, CRL_OK );
 
@@ -317,16 +348,26 @@
 			continue;
 		}
 
+		if(ca->revocation_check && !ca->crl_fallback) {
+			if( ocspd_conf->verbose )
+				PKI_log(PKI_LOG_INFO, "INFO::No CRL configured for CA [%s] - skipping reload",
+							ca->ca_id );
+			continue;
+		}
+
 		if( ocspd_conf->verbose && ca->ca_id )
 			PKI_log(PKI_LOG_INFO, "Auto CRL checking [%s]", ca->ca_id);
 
 
-		ret = check_crl_validity ( ca, ocspd_conf );
+		ret = check_crl_validity ( ca->crl_data, &ca->single_crl_lock, ocspd_conf, ca->ca_id );
+
+		PKI_RWLOCK_read_lock ( &ca->single_crl_lock );
+    ret -= ca->crl_data->crl_status;
+		PKI_RWLOCK_release_read ( &ca->single_crl_lock );
 
-		if( ca->crl_status != ret ) {
+		if( ret != 0 ) {
 			if(ocspd_conf->verbose) 
 				PKI_log(PKI_LOG_INFO,"Detected CRL status change");
-			ca->crl_status = ret;
 
 			ocspd_load_ca_crl (ca, ocspd_conf);
 
@@ -336,20 +377,7 @@
 				PKI_log(PKI_LOG_INFO,"No CRL status change for [%s]",
 					ca->ca_id);
 		}
-		// syslog( LOG_INFO, "Forcing CRL Reloading for [%s]",
-		// 	ca->ca_id ? ca->ca_id : "No Name" );
-		// ocspd_load_ca_crl (ca, ocspd_conf);
-	}
-
-	/*
-	if( ocspd_conf->crl_check_validity ) {
-		if( verbose )
-			syslog(LOG_INFO, "Checking again CRL in %d secs",
-				ocspd_conf->crl_check_validity );
-
-		alarm( ocspd_conf->crl_check_validity );
 	}
-	*/
 
 	if( ocspd_conf->verbose == 1 ) {
 		PKI_log(LOG_INFO, "auto_crl_check() completed");
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/includes/configuration.h openca-ocspd-3.1.2/src/ocspd/includes/configuration.h
--- openca-ocspd-3.1.2-orig/src/ocspd/includes/configuration.h	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/includes/configuration.h	2023-07-14 16:39:36.408164743 +0200
@@ -20,8 +20,14 @@
 
 #include "general.h"
 
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+#include <openssl/ts.h>
+#endif
+
 OCSPD_CONFIG * OCSPD_load_config( char *configfile );
 
+void OCSPD_free_config(OCSPD_CONFIG *cnf);
+
 int OCSPD_build_ca_list ( OCSPD_CONFIG *handler,
 				PKI_CONFIG_STACK *ca_conf_sk);
 
@@ -31,8 +37,7 @@
 
 int ocspd_load_ca_section ( OCSPD_CONFIG *conf, char *dbms_section );
 
-STACK_OF(X509_REVOKED) *ocspd_build_crl_entries_list ( CA_LIST_ENTRY *ca,
-				PKI_X509_CRL *crl );
+STACK_OF(X509_REVOKED) *ocspd_build_crl_entries_list ( PKI_X509_CRL *crl, char *ca_id );
 
 CA_LIST_ENTRY * CA_LIST_ENTRY_new ( void );
 
@@ -41,9 +46,12 @@
 CA_LIST_ENTRY * OCSPD_ca_entry_new ( OCSPD_CONFIG *handler,
 				PKI_X509_CERT *x, PKI_CONFIG *cnf );
 
-CA_ENTRY_CERTID * CA_ENTRY_CERTID_new ( PKI_X509_CERT *x, 
-				PKI_DIGEST_ALG * digest );
+STACK_OF(CA_ENTRY_CERTID) * CA_ENTRY_CERTID_new_sk ( PKI_X509_CERT *cert, 
+					STACK_OF(EVP_MD) *mds );
 
 void CA_ENTRY_CERTID_free ( CA_ENTRY_CERTID *cid );
 
+void CRL_DATA_free ( CRL_DATA *crl_data );
+void CRL_DATA_free_all ( CRL_DATA *crl_data );
+
 #endif
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/includes/crl.h openca-ocspd-3.1.2/src/ocspd/includes/crl.h
--- openca-ocspd-3.1.2-orig/src/ocspd/includes/crl.h	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/includes/crl.h	2023-07-14 16:39:36.408164743 +0200
@@ -20,8 +20,8 @@
 
 int ocspd_load_ca_crl ( CA_LIST_ENTRY *ca, OCSPD_CONFIG *conf );
 int ocspd_reload_crls ( OCSPD_CONFIG *conf );
-int check_crl ( PKI_X509_CRL *crl, PKI_X509_CERT *ca, OCSPD_CONFIG *conf );
-int check_crl_validity ( CA_LIST_ENTRY *ca, OCSPD_CONFIG *conf );
+int check_crl ( PKI_X509_CRL *x_crl, PKI_X509_CERT *x_cacert, PKI_RWLOCK *single_crl_lock, OCSPD_CONFIG *conf );
+int check_crl_validity ( CRL_DATA *crl_data, PKI_RWLOCK *lock, OCSPD_CONFIG *conf, char *ca_id );
 char * get_crl_status_info ( int status );
 void force_crl_reload ( int sig );
 
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/includes/general.h openca-ocspd-3.1.2/src/ocspd/includes/general.h
--- openca-ocspd-3.1.2-orig/src/ocspd/includes/general.h	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/includes/general.h	2025-11-07 15:17:17.099865042 +0100
@@ -53,33 +53,132 @@
 #define OCSPD_SRV_OK	0
 #define OCSPD_SRV_ERR	-1
 
+/* OCSP certificate information (used for statistics) */
+typedef struct ocspd_cert_info {
+	int                 cert_status;  // certificate status (revoked, unknown, ok)
+	char                *issuer;      // issuer of requested certificate
+	char                *ca_id;       // CA config name (caConfig->name)
+	char                *serial;      // certificate serial
+	unsigned char       *cert_type;   // certificate type
+	char                *ca_ski;      // CA Subject Key Identifier
+	int                 cached;       // comes from cache?
+	long                q_duration;   // duration of the database query in nanoseconds
+} OCSPD_CERT_INFO;
+
+/* OCSP session information (used for statistics) */
+typedef struct ocspd_session_info {
+	struct sockaddr_in  cliaddr;      // client IP (requester)
+	struct sockaddr_in  srvaddr;      // server IP and port
+	int                 resp_status;  // response status (malformedRequest, internalError, tryLater, sigRequired, unauthorized)
+	struct timespec     start_recv;   // timestamp when the connection is established
+	struct timespec     start;        // timestamp when the request has arrived
+	struct timespec     stop;         // timestamp when the response was sent
+	unsigned int        duration;     // difference between stop and start
+	STACK_OF(void)      *sk_cert_info;// list of OCSPD_CERT_INFO (for each certificate contained in the OCSP request)
+} OCSPD_SESSION_INFO;
+
+/* statistics related definitions */
+/* DON'T MODIFY THE ORDERING OF THIS LIST, THE PROGRAM CODE RELIES ON IT! */
+#define OCSPD_STATS_INFO_STARTTIME       0x0001
+#define OCSPD_STATS_INFO_ENDTIME         0x0002
+#define OCSPD_STATS_INFO_RESPONSE_STATUS 0x0004
+#define OCSPD_STATS_INFO_CERT_STATUS     0x0008
+#define OCSPD_STATS_INFO_SERIAL          0x0010
+#define OCSPD_STATS_INFO_ISSUER          0x0020
+#define OCSPD_STATS_INFO_CANAME          0x0040
+#define OCSPD_STATS_INFO_IP              0x0080
+#define OCSPD_STATS_INFO_DURATION        0x0100
+#define OCSPD_STATS_RESPONDER_NAME       0x0200
+#define OCSPD_STATS_ARRIVAL_TIME         0x0400
+#define OCSPD_STATS_DEPARTURE_TIME       0x0800
+#define OCSPD_STATS_CERT_TYPE            0x1000
+#define OCSPD_ATTR_CERT_TYPE             OCSPD_STATS_CERT_TYPE
+#define OCSPD_ATTR_CERT_STATUS           OCSPD_STATS_INFO_CERT_STATUS
+#define OCSPD_ATTR_REVOCATION_DATE       0x2000
+#define OCSPD_ATTR_FINGERPRINT           0x4000
+#define OCSPD_STATS_INFO_SKI             0x8000
+
+typedef struct {
+	char *name;         // item name
+	char *column;       // column name
+	char *entry;        // selected entry
+	unsigned long flag; // item flag
+} LOG_STATS_ITEM_TBL;
+
+static const LOG_STATS_ITEM_TBL ocsp_stats_item_tbl[] = {
+	{ "StartTime",      "starttime",            NULL, OCSPD_STATS_INFO_STARTTIME},
+	{ "EndTime",        "endtime",              NULL, OCSPD_STATS_INFO_ENDTIME},
+	{ "ResponseStatus", "response_status",      NULL, OCSPD_STATS_INFO_RESPONSE_STATUS},
+	{ "CertStatus",     "cert_status",          NULL, OCSPD_STATS_INFO_CERT_STATUS},
+	{ "Serial",         "serialnumber",         NULL, OCSPD_STATS_INFO_SERIAL},
+	{ "Issuer",         "issuer",               NULL, OCSPD_STATS_INFO_ISSUER},
+	{ "CaName",         "caname",               NULL, OCSPD_STATS_INFO_CANAME},
+	{ "IP",             "ip",                   NULL, OCSPD_STATS_INFO_IP},
+	{ "Duration",       "duration",             NULL, OCSPD_STATS_INFO_DURATION},
+	{ "ResponderName",  "responder_name",       NULL, OCSPD_STATS_RESPONDER_NAME},
+	{ "ArrivalTime",    "arrival_time",         NULL, OCSPD_STATS_ARRIVAL_TIME},
+	{ "DepartureTime",  "departure_time",       NULL, OCSPD_STATS_DEPARTURE_TIME},
+	{ "CertType",       "cert_type",            NULL, OCSPD_ATTR_CERT_TYPE},
+	{ "RevDate",        "rev_date",             NULL, OCSPD_ATTR_REVOCATION_DATE},
+	{ "Fingerprint",    "fingerprint",          NULL, OCSPD_ATTR_FINGERPRINT},
+	{ "SKI",            "ski",                  NULL, OCSPD_STATS_INFO_SKI},
+	{ NULL,             NULL,                   NULL, 0},
+};
+
+typedef struct revocation_check
+{
+	size_t  nval;
+	char **value;
+	int  negate;  // 1: negate check
+} REVOCATION_CHECK;
+
+typedef struct ignore_ip_list
+{
+	size_t  nval;
+	struct in_addr **value;  // ignore ip for statistics logging
+} IGNORE_IP_LIST;
+
 typedef struct crl_data
 	{
-		/* CRL access method OCSP_CRL_METHOD_... */
-		/* Filename */
-		URL *url;
+		/* Pure CRL data */
+		PKI_X509_CRL *crl;
 
-		X509_CRL *crl;
-	} CRL_DATA;
+		/* Pointer to the list of CRLs entries */
+		STACK_OF(X509_REVOKED) *crl_list;
 
-typedef struct x509_crl_entry 
-	{
-		long reason;
+		/* Number of entries present in the list */
+		//unsigned long entries_num;
 
-		/* Serial Number of the entry */
-		ASN1_INTEGER *serial;
+		/* X509 nextUpdate and lastUpdate */
+		PKI_TIME *nextUpdate;
+		PKI_TIME *lastUpdate;
 
-		/* Revocation Time */
-		ASN1_TIME *rev_time;
+		/* Options for auto reloading of CRL upon expiration */
+		int crl_status;
 
-		/* Invalidity time, if reason in KeyTime or
-		 * CAKeyTime  */
-		ASN1_GENERALIZEDTIME *invalidity_time;
+		/* last mtime from loaded CRL */
+		time_t mtime;
 
-		/* Hold Instruction, if present */
-		ASN1_OBJECT *hold_instr;
+	} CRL_DATA;
 
-	} X509_CRL_ENTRY;
+//typedef struct x509_crl_entry 
+//	{
+//		long reason;
+//
+//		/* Serial Number of the entry */
+//		ASN1_INTEGER *serial;
+//
+//		/* Revocation Time */
+//		ASN1_TIME *rev_time;
+//
+//		/* Invalidity time, if reason in KeyTime or
+//		 * CAKeyTime  */
+//		ASN1_GENERALIZEDTIME *invalidity_time;
+//
+//		/* Hold Instruction, if present */
+//		ASN1_OBJECT *hold_instr;
+//
+//	} X509_CRL_ENTRY;
 
 typedef struct ca_entry_certid
 	{
@@ -97,11 +196,12 @@
 		ASN1_OCTET_STRING *nameHash;
 
 		// Holds the indication for the current status of the CRL
-		int crl_status;
+		//int crl_status;
 
 	} CA_ENTRY_CERTID;
 
 #define sk_CA_ENTRY_CERTID_new_null() SKM_sk_new_null(CA_ENTRY_CERTID)
+#define sk_CA_ENTRY_CERTID_free(st) SKM_sk_free(CA_ENTRY_CERTID, (st))
 #define sk_CA_ENTRY_CERTID_push(st, val) SKM_sk_push(CA_ENTRY_CERTID, (st), (val))
 #define sk_CA_ENTRY_CERTID_pop(st) SKM_sk_pop(CA_ENTRY_CERTID, (st))
 #define sk_CA_ENTRY_CERTID_value(st, i) SKM_sk_value(CA_ENTRY_CERTID, (st), (i))
@@ -109,19 +209,36 @@
 #define sk_CA_ENTRY_CERTID_sort(st) SKM_sk_sort(CA_ENTRY_CERTID, (st))
 #define sk_CA_ENTRY_CERTID_find(st) SKM_sk_find(CA_ENTRY_CERTID, (st))
 
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+DECLARE_STACK_OF(EVP_MD)
+DECLARE_ASN1_SET_OF(EVP_MD)
+#define sk_EVP_MD_new_null() SKM_sk_new_null(EVP_MD)
+#define sk_EVP_MD_free(st) SKM_sk_free(EVP_MD, (st))
+#define sk_EVP_MD_push(st, val) SKM_sk_push(EVP_MD, (st), (val))
+#define sk_EVP_MD_pop(st) SKM_sk_pop(EVP_MD, (st))
+#define sk_EVP_MD_value(st, i) SKM_sk_value(EVP_MD, (st), (i))
+#define sk_EVP_MD_num(st) SKM_sk_num(EVP_MD, (st))
+#define sk_EVP_MD_sort(st) SKM_sk_sort(EVP_MD, (st))
+#define sk_EVP_MD_find(st) SKM_sk_find(EVP_MD, (st))
+#endif
+
 /* List of available CAs */
 typedef struct ca_list_st {
 	/* CA Identifier - Name from config file */
 	char *ca_id;
 
+	/* CA DN (resp. CN) */
+	char *ca_cn; 
+
 	/* CA Status - If compromised > 0 respond all revoked */
 	int compromised;
+	PKI_TIME *compromised_date;
 
 	/* CA certificate */
 	PKI_X509_CERT *ca_cert;
 
 	/* Cert Identifier */
-	CA_ENTRY_CERTID *cid;
+	STACK_OF(CA_ENTRY_CERTID) *sk_cid;
 
 	/* CA certificate URL */
 	URL *ca_url;
@@ -129,21 +246,11 @@
 	/* CRL URL */
 	URL *crl_url;
 
-	/* CRL data */
-	PKI_X509_CRL *crl;
+	/* to lock the single CRL of a specific CA */
+	PKI_RWLOCK single_crl_lock;
 
-	/* Pointer to the list of CRLs entries */
-	STACK_OF(X509_REVOKED) *crl_list;
-
-	/* X509 nextUpdate and lastUpdate */
-	PKI_TIME *nextUpdate;
-	PKI_TIME *lastUpdate;
-
-	/* Options for auto reloading of CRL upon expiration */
-	int crl_status;
-
-	/* Number of entries present in the list */
-	unsigned long entries_num;
+	/* CRL data - every access must be locked using single_crl_lock */
+	CRL_DATA  *crl_data;
 
 	/* TOKEN to be used with this CA - if null, the default
          * one will be used */
@@ -153,29 +260,56 @@
 	char *token_config_dir;
 	PKI_TOKEN *token;
 	
+	int check_issued_by_ca;
+	int crl_fallback;
+	PKI_DIGEST *ca_cert_digest;
+	char ca_cert_digest_str[EVP_MAX_MD_SIZE+1];
+	char *ca_cert_ski; // subjectKeyIdentifier
+	int db_ctimeout; // connection timeout
+	unsigned long  db_attr_flags; // database attributes - mask table
+	URL *db_url;
+	URL *db_url2;
+	unsigned long num_db_attr; // number of attributes that shall be returned from the SQL query (everything after the '?')
+	REVOCATION_CHECK *revocation_check;
+	unsigned long issuer_dn_nmflag;
+	char *db_column_ca_fingerprint;
+	char *db_column_issuer_name_hash;
+	char *db_column_subject_key_identifier;
+	char *db_column_serial_number;
+	char *db_column_issuer_dn;
+
+	/* certStatus */
+	char *db_column_cert_status;
+	char *cert_status_check_value;;
+
+	/* certHash */
+	EVP_MD *cert_hash_digest;
+
+	/* archiveCutoff */
+	char *archive_cutoff;
+
+	/* nextUpdateExpired */
+	PKI_TIME *next_update_expired;
+
 	/* Responder Identifier Type */
 	int response_id_type;
 
+	/* Ignore CRL reason code in OCSP response - CA specific configuration */
+	int ignore_reason_code;
+
+	/* certType */
+	char *cert_type;
+
+	/* certStatus default response */
+	int cert_status_default;
+
 } CA_LIST_ENTRY;
 
 typedef struct {
-	pthread_t thread_tid;
+	PKI_THREAD thread_tid;
 	long thread_count;
 } Thread;
 
-typedef struct {
-	int iget;
-	int iput;
-
-	int connfd;
-	int listenfd;
-	int nthreads;
-
-	int *clifd;
-
-	Thread *threads_list;
-} OPENCA_GENCFG;
-
 typedef struct ocspd_config {
 
 	/* Configuration file name */
@@ -190,25 +324,30 @@
 	int verbose;
 	int debug;
 	int testmode;
+	int valgrind;
+	int daemon;
 
 	/* Default Response's validity time */
 	int nmin;
 	int ndays;
 	int set_nextUpdate;
+	int ignore_reason_code;
 
 	int flags;
 
-	// CRL_DATA crl_data;
-
 	/* User and Group the processes will run as */
 	char *user;
 	char *group;
 	char *chroot_dir;
 
 	/* Digest to be used */
+	STACK_OF(EVP_MD) *issuerHashDigest;
 	PKI_DIGEST_ALG *digest;
 	PKI_DIGEST_ALG *sigDigest;
 
+	/* OCSP responder name - used for logging */
+	char *responder_name;
+
 	/* OCSP responder default token */
 	char *token_name;
 	char *token_config_dir;
@@ -221,25 +360,40 @@
 	int crl_check_validity;
 	int crl_auto_reload;
 	int crl_reload_expired;
+	int crl_check_mtime;
 
 	int current_crl_reload;
 	int current_crl_check;
 	int alarm_decrement;
 
+	int log_timing;
+
 	/* DataBase Related */
+	URL *log_stats_url;
+	unsigned long  log_stats_flags; // mask table
+	unsigned int  log_stats_ignore_failed_connections;
+	unsigned int  log_stats_dn; // True / False
+	int db_ctimeout; // connection timeout
 	URL *db_url;
 	int db_persistant;
+	IGNORE_IP_LIST *ignore_ip_list;  // ignore ip for statistics logging
 
-	/* Network related */
+	/* Request related */
+	ssize_t max_nonce_length;
 	ssize_t max_req_size;
+
+	/* Network related */
 	unsigned int max_timeout_secs;
 	char * http_proto;
 	char * base_uri;
 	URL  * bindUrl;
+	URL  * healthCheckUrl;
+	int   http_persistent;
 
 	int *clifd;
 	int connfd;
 	int listenfd;
+	int listenfd_hc;
 
 	int nthreads;
 	Thread *threads_list;
@@ -247,12 +401,20 @@
 	PKI_MUTEX mutexes[3];
 	PKI_COND  condVars[2];
 
-	// PKI_RWLOCK config_lock;
-	PKI_RWLOCK crl_lock;
+	/* maximum supported serial number contained in requests */
+	BIGNUM *max_serial;
+
+	/* maximum number of requests contained in a single OCSP request */
+	int max_request_count;
+
+	/* Cache settings to be passed to libmemcached */
+	char *cache_settings;
+
+	/* Cache responses for x seconds (0 disables cache) */
+	time_t cache_duration;
 
 } OCSPD_CONFIG;
 
-#define CTRL_MUTEX      0
 #define CLIFD_MUTEX     1
 #define SRVFD_MUTEX     2
 
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/includes/request.h openca-ocspd-3.1.2/src/ocspd/includes/request.h
--- openca-ocspd-3.1.2-orig/src/ocspd/includes/request.h	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/includes/request.h	2025-09-26 17:22:05.948976930 +0200
@@ -2,7 +2,7 @@
 #ifndef __OCSPD_REQUEST_H
 #define __OCSPD_REQUEST_H
 
-PKI_X509_OCSP_REQ * ocspd_req_get_socket(int connfd, OCSPD_CONFIG *ocspd_conf);
+PKI_X509_OCSP_REQ * ocspd_req_get_socket(int connfd, OCSPD_CONFIG *ocspd_conf, OCSPD_SESSION_INFO *sinfo);
 
 #endif
 
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/includes/response.h openca-ocspd-3.1.2/src/ocspd/includes/response.h
--- openca-ocspd-3.1.2-orig/src/ocspd/includes/response.h	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/includes/response.h	2023-07-14 16:39:36.408164743 +0200
@@ -13,8 +13,8 @@
 
 /* Functions */
 
-PKI_X509_OCSP_RESP *make_ocsp_response( PKI_X509_OCSP_REQ *req, 
-						OCSPD_CONFIG *conf );
+PKI_X509_OCSP_RESP *make_ocsp_response(PKI_X509_OCSP_REQ *req, OCSPD_CONFIG *conf,
+						OCSPD_SESSION_INFO *sinfo);
 
 int ocspd_resp_send_socket(int connfd, PKI_X509_OCSP_RESP *resp, 
 						OCSPD_CONFIG *conf);
@@ -22,4 +22,4 @@
 /* ------------------------- Find Functions ---------------------------- */
 
 X509_REVOKED *OCSPD_REVOKED_find (CA_LIST_ENTRY *ca, ASN1_INTEGER *serial);
-CA_LIST_ENTRY *OCSPD_CA_ENTRY_find ( OCSPD_CONFIG *conf, OCSP_CERTID *cid );
+//CA_LIST_ENTRY *OCSPD_CA_ENTRY_find ( OCSPD_CONFIG *conf, OCSP_CERTID *cid );
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/ocspd.c openca-ocspd-3.1.2/src/ocspd/ocspd.c
--- openca-ocspd-3.1.2-orig/src/ocspd/ocspd.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/ocspd.c	2025-08-26 16:52:38.163092209 +0200
@@ -22,26 +22,21 @@
 "   USAGE: ocspd args\n",
 "\n",
 " -c file         - The configuration file\n",
+" -logfile file   - Logfile to write messages to (if not given defaults to syslog)\n",
+" -logident ident - Identifier used to register to syslog (default to program name)\n",
 " -d              - Daemon, detach from current console\n",
 " -r dir          - Directory where to jail the running process (chroot)\n",
-" -k pwd          - Password protecting the private key (if any)\n",
 " -debug          - Debug mode (exit after the first request)\n",
-" -testmode       - Use test mode (wrong signatures w/ 1st bit flipped\n",
+" -testmode       - Use test mode (wrong signatures w/ 1st bit flipped)\n",
 " -stdout         - Route all logging messages to stdout\n",
 " -v              - Talk alot while doing things\n",
 NULL
 };
 
-/* Staic variables */
-char *prgname = "ocspd";
-char *version = VERSION;
-
 OCSPD_CONFIG *ocspd_conf = NULL;
 
 /* Local functions prototypes */
 int  writePid(int pid, char *pidfile);
-void my_exit(int cod, char *txt);
-void mask_signals();
 
 /* Main */
 int main ( int argc, char *argv[] ) {
@@ -52,20 +47,20 @@
 	int verbose   = 0;
 	int debug     = 0;
 	int testmode  = 0;
+	int valgrind  = 0;
 
 	int daemon = 0;
 	int badops = 0;
-	int ret = 0;
+	int ret    = EXIT_FAILURE;
 
 	char *configfile = NULL;
+	char *logfile = NULL;
 	char **pp = NULL;
 
 	int i = 0;
 	pid_t pid = 0;
 	pid_t ppid = 0;
 
-	/* Let's init LibPKI */
-	PKI_init_all();
 
 	argv++;
 	argc--;
@@ -76,13 +71,30 @@
 		{
 			if (--argc < 1) goto bad;
 			configfile= *(++argv);
+		} else if (strcmp(*argv,"-logfile") == 0) {
+			if (--argc < 1) goto bad;
+			logfile= *(++argv);
+			log_type = PKI_LOG_TYPE_FILE;
+		} else if (strcmp(*argv,"-logident") == 0) {
+			if (--argc < 1) goto bad;
+			logfile= *(++argv);
+			log_type = PKI_LOG_TYPE_SYSLOG;
 		} else if (strcmp(*argv,"-v") == 0) {
-			log_level=PKI_LOG_INFO;
+			if(log_level == PKI_LOG_NOTICE)
+				log_level=PKI_LOG_INFO;
+			else
+				log_level |= PKI_LOG_INFO;
 			verbose = 1;
 		} else if (strcmp(*argv,"-debug") == 0) {
+			if(log_level == PKI_LOG_NOTICE)
+				log_level = PKI_LOG_DEBUG;
+			else
+				log_level |= PKI_LOG_DEBUG;
 			debug=1;
 		} else if (strcmp(*argv,"-testmode") == 0) {
 			testmode=1;
+		} else if (strcmp(*argv,"-valgrind") == 0) {
+			valgrind=1;
 		} else if (strcmp(*argv,"-d") == 0) {
 			daemon=1;
 		} else if (strcmp(*argv,"-stdout") == 0) {
@@ -102,23 +114,27 @@
 		{
 			fprintf(stderr, "%s", *pp);
 		}
-		goto err;
+		return (ret);
 	}
 
 	if( daemon == 0 ) {
 		fprintf(stderr, ocspd_warning, VERSION);
 	}
 
-	if(( PKI_log_init (log_type, log_level, NULL,
+	// PKI_log_init() calls also PKI_init_all()
+	if(( PKI_log_init (log_type, log_level, logfile,
 			debug, NULL )) == PKI_ERR ) {
 		fprintf(stderr, "OCSPD, can not initiating logs! Aborting!\n\n");
-		exit(1);
+		goto err;
 	}
 
-	PKI_log(PKI_LOG_ALWAYS, "OpenCA OCSPD v%s - starting.", VERSION );
+	PKI_log(PKI_LOG_ALWAYS, "OpenCA OCSPD v%s - starting (%s).", VERSION, SSLeay_version(SSLEAY_VERSION));
+	PKI_log(PKI_LOG_ALWAYS, "OpenCA OCSPD adapted by Atos. Version p1.1.11 (26 Aug 2025).");
 
 	if(( ocspd_conf = OCSPD_load_config( configfile )) == NULL ) {
-		my_exit(1, "ERROR::can not load config file!\n");
+		fprintf(stderr, "ERROR::can not load config file!\n\n");
+		PKI_log_err("ERROR::can not load config file!");
+		goto err;
 	}
 
 	if( debug ) ocspd_conf->debug = 1;
@@ -128,7 +144,13 @@
 		PKI_log(PKI_LOG_ALWAYS, "WARNING: Test Mode Used, All Signatures "
 			"will be INVALID (first bit flipped)");
 		ocspd_conf->testmode = 1;
-	};
+	}
+
+	if( valgrind ) 
+	{
+		PKI_log(PKI_LOG_ALWAYS, "WARNING: Valgrind Mode - Use pthread_exit() for smooth cleanup");
+		ocspd_conf->valgrind = 1;
+	}
 
 
 	/*****************************************************************/
@@ -143,8 +165,10 @@
 			/* Main process, we have to save the pid to the
 			 * pidfile and then exit */
 			writePid( getpid(), ocspd_conf->pidfile );
+			ocspd_conf->daemon = 1;
 		} else if ( pid > 0 ) {
 			/* Nop */
+			ret = EXIT_SUCCESS;
 			goto end;
 		} else {
 			PKI_log_err("Error While spawning child %d", i );
@@ -155,20 +179,23 @@
 		writePid( ppid, ocspd_conf->pidfile );
 	}
 
-	// Mask un-wanted signals
-	mask_signals();
-
 	// Let's now start the threaded server
-	start_threaded_server( ocspd_conf );
-
-	goto end;
-
+	if( start_threaded_server( ocspd_conf ) == PKI_OK)
+	{
+		ret = EXIT_SUCCESS;
+		goto end;
+	}
 
 err:
-	ret = -1;
-	my_exit( -1, "ERROR:General error, please check the logs");
+	PKI_log_err("ERROR:General error, please check the logs");
 
 end:
+	if(ocspd_conf) OCSPD_free_config(ocspd_conf);
+	if(ocspd_conf) PKI_Free(ocspd_conf);
+	PKI_final_all();
+	PKI_final_thread();  // also needed for the main thread
+	PKI_log_end();
+
 	return (ret);
 }
 
@@ -193,21 +220,3 @@
 	return(1);
 }
 
-void mask_signals()
-{
-	struct sigaction sa;
-
-	sa.sa_handler = SIG_IGN;
-	sa.sa_flags = 0;
-
-	if (sigaction(SIGPIPE, &sa, 0) == -1)
-	{
-		PKI_log(PKI_LOG_ERR, "Can not mask SIGPIPE, aborting!\n\n");
-		exit(1);
-	}
-}
-
-void my_exit(int cod, char *txt) {
-	PKI_log(PKI_LOG_ERR, "%s - %s (exit with %d)\n\n", prgname, txt, cod );
-	exit(cod);
-}
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/request.c openca-ocspd-3.1.2/src/ocspd/request.c
--- openca-ocspd-3.1.2-orig/src/ocspd/request.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/request.c	2025-09-26 17:22:05.948976930 +0200
@@ -17,9 +17,11 @@
 #define  MAX_USEC		1000
 #define  WAIT_USEC		50
 
+#define MAX_LOG_TRACE_SIZE	256
+
 extern OCSPD_CONFIG *ocspd_conf;
 
-PKI_X509_OCSP_REQ * ocspd_req_get_socket ( int connfd, OCSPD_CONFIG *ocspd_conf) 
+PKI_X509_OCSP_REQ * ocspd_req_get_socket ( int connfd, OCSPD_CONFIG *ocspd_conf, OCSPD_SESSION_INFO *sinfo) 
 {
 	PKI_X509_OCSP_REQ 	*req = NULL;
 	PKI_X509_OCSP_REQ_VALUE *req_val = NULL;
@@ -33,20 +35,32 @@
 	maxsize = (size_t) ocspd_conf->max_req_size;
 
 	PKI_HTTP *http_msg = NULL;
+	char *p_data = NULL;
+	int  len = 0;
+
 
 	if ( connfd <= 0 ) return NULL;
 
 	// Initialize the sock structure
-	sock.ssl = NULL;
+	memset(&sock, 0, sizeof(sock));
 	PKI_SOCKET_set_fd ( &sock, connfd );
 
 	http_msg = PKI_HTTP_get_message(&sock, (int) ocspd_conf->max_timeout_secs, maxsize);
 	if (http_msg == NULL)
 	{
-		PKI_log_err ("Network Error while reading Request!");
+		PKI_log_debug ("Network Error while reading Request on port %u!", ntohs(sinfo->srvaddr.sin_port));
 		return NULL;
 	}
 
+	// Set start timer after receiving but before parsing the whole request
+	if (clock_gettime(CLOCK_REALTIME, &sinfo->start) != 0)
+	{
+		char err_str[128];
+
+		PKI_strerror(errno, err_str, sizeof err_str);
+		PKI_log_err("Setting start timer with clock_gettime() failed [%d::%s]", errno, err_str);
+	}
+
 	/* If method is METHOD_GET we shall de-urlify the buffer and get the
 	   right begin (keep in mind there might be a path set in the config */
 
@@ -61,21 +75,42 @@
 		}
 		
 		req_pnt = http_msg->path;
-		while(strchr(req_pnt, '/') != NULL)
+		if(strncmp_nocase(req_pnt, "http://", 7) == 0)
+			req_pnt += 7;
+		else if(strncmp_nocase(req_pnt, "https://", 8) == 0)
+			req_pnt += 8;
+
+		/* Skip leading '/' for the path */
+		while( *req_pnt == '/' )
+			req_pnt++;
+
+		if(strlen(req_pnt) == 0)
 		{
-			req_pnt = strchr(req_pnt, '/') + 1;
+			PKI_log(PKI_LOG_INFO, "HTTP GET URL does not contain a valid OCSP request");
+			if( ocspd_conf->debug )
+			{
+				if(strlen(req_pnt) > MAX_LOG_TRACE_SIZE)
+					PKI_log_hexdump(PKI_LOG_INFO, "HTTP GET URL (truncated)", MAX_LOG_TRACE_SIZE, http_msg->path);
+				else
+					PKI_log_hexdump(PKI_LOG_INFO, "HTTP GET URL", (int) strlen(http_msg->path), http_msg->path);
+			}
+			goto err;
 		}
 
 		pathmem = PKI_MEM_new_data(strlen(req_pnt), (unsigned char *) req_pnt);
 		if (pathmem == NULL)
 		{
-			PKI_log_err("Memory Allocation Error!");
+			PKI_log_err("Memory allocation failed for %d bytes!", strlen(req_pnt));
+			if(strlen(req_pnt) > MAX_LOG_TRACE_SIZE)
+				PKI_log_hexdump(PKI_LOG_INFO, "PKI_MEM_new_data() (truncated)", MAX_LOG_TRACE_SIZE, req_pnt);
+			else
+				PKI_log_hexdump(PKI_LOG_INFO, "PKI_MEM_new_data()", (int) strlen(req_pnt), req_pnt);
 			goto err;
 		}
 
 		if (PKI_MEM_decode(pathmem, PKI_DATA_FORMAT_URL, 0) != PKI_OK)
 		{
-			PKI_log_err("Memory Allocation Error!");
+			PKI_log_err("URL decode failed!");
 			PKI_MEM_free(pathmem);
 			goto err;
 		}
@@ -83,26 +118,64 @@
 		if (PKI_MEM_decode(pathmem, PKI_DATA_FORMAT_B64, 0) != PKI_OK)
 		{
 			PKI_log_err ("Error decoding B64 Mem");
+
+			if(pathmem->size > MAX_LOG_TRACE_SIZE)
+				PKI_log_hexdump(PKI_LOG_INFO, "PKI_MEM_B64_decode1() (truncated)", MAX_LOG_TRACE_SIZE, pathmem->data);
+			else
+				PKI_log_hexdump(PKI_LOG_INFO, "PKI_MEM_B64_decode1()", (int) pathmem->size, pathmem->data);
+
 			PKI_MEM_free (pathmem);
 			goto err;
 		}
 
+		if(pathmem->size == 0)
+		{
+			PKI_log(PKI_LOG_INFO, "No data decoded from GET request");
+
+			if( ocspd_conf->debug )
+			{
+				if(strlen(req_pnt) > MAX_LOG_TRACE_SIZE)
+					PKI_log_hexdump(PKI_LOG_INFO, "Encoded URL (truncated)", MAX_LOG_TRACE_SIZE, req_pnt);
+				else
+					PKI_log_hexdump(PKI_LOG_INFO, "Encoded URL", (int) strlen(req_pnt), req_pnt);
+			}
+
+			PKI_MEM_free(pathmem);
+			goto err;
+		}
+
 		// Generates a new mem bio from the pathmem
 		if((mem = BIO_new_mem_buf(pathmem->data, (int) pathmem->size)) == NULL)
 		{
-			PKI_log_err("Memory Allocation Error");
+			PKI_log_err("Memory allocation failed for decoded URL path (%d bytes)", pathmem->size);
+
+			if(strlen(req_pnt) > MAX_LOG_TRACE_SIZE)
+				PKI_log_hexdump(PKI_LOG_INFO, "Encoded URL (truncated)", MAX_LOG_TRACE_SIZE, req_pnt);
+			else
+				PKI_log_hexdump(PKI_LOG_INFO, "Encoded URL", (int) strlen(req_pnt), req_pnt);
+
 			PKI_MEM_free(pathmem);
 			goto err;
 		}
 
 		// Transfer data ownership and release the pathmem
-		pathmem->data = NULL;
-		pathmem->size = 0;
+		// BIO_new_mem_buf() creates a new memory buffer - therefore this is not needed
+		//pathmem->data = NULL;
+		//pathmem->size = 0;
+
+		if( (len = (int)BIO_get_mem_data(mem, &p_data) ) <= 0)
+			PKI_log_debug ( "BIO_get_mem_data(GET) failed");
 
 		// Tries to decode the binary (der) encoded request
 		if ((req_val = d2i_OCSP_REQ_bio(mem, NULL)) == NULL)
 		{
-			PKI_log_err("Can not parse REQ");
+			PKI_log(PKI_LOG_INFO, "Can not parse REQ");
+
+			if(len > MAX_LOG_TRACE_SIZE)
+				PKI_log_hexdump(PKI_LOG_INFO, "d2i_OCSP_REQ_bio(GET) (truncated)", MAX_LOG_TRACE_SIZE, p_data);
+			else
+				PKI_log_hexdump(PKI_LOG_INFO, "d2i_OCSP_REQ_bio(GET)", len, p_data);
+
 			BIO_free(mem);
 			PKI_MEM_free(pathmem);
 			goto err;
@@ -115,17 +188,31 @@
 	} 
 	else if (http_msg->method == PKI_HTTP_METHOD_POST)
 	{
+		if(http_msg->body->size <= 0)
+		{
+			PKI_log_debug ( "HTTP POST message: Body does not contain any data.");
+			goto err;
+		}
+
 		mem = BIO_new_mem_buf(http_msg->body->data, (int) http_msg->body->size);
 		if (mem == NULL)
 		{
-			PKI_log_err( "Memory Allocation Error");
+			PKI_log_err( "Memory allocation failed for HTTP POST message (allocating %d bytes).", http_msg->body->size);
 			goto err;
 		}
 		else
 		{
+			if( (len = (int)BIO_get_mem_data(mem, &p_data) ) <= 0)
+				PKI_log_debug ( "BIO_get_mem_data(POST) failed");
+
 			if ((req_val = d2i_OCSP_REQ_bio(mem, NULL)) == NULL)
 			{
 				PKI_log_err("Can not parse REQ");
+
+				if(len > MAX_LOG_TRACE_SIZE)
+					PKI_log_hexdump(PKI_LOG_INFO, "d2i_OCSP_REQ_bio(POST) (truncated)", MAX_LOG_TRACE_SIZE, p_data);
+				else
+					PKI_log_hexdump(PKI_LOG_INFO, "d2i_OCSP_REQ_bio(POST)", len, p_data);
 			}
 			BIO_free (mem);
 		}
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/response.c openca-ocspd-3.1.2/src/ocspd/response.c
--- openca-ocspd-3.1.2-orig/src/ocspd/response.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/response.c	2025-10-07 17:43:57.451369252 +0200
@@ -7,6 +7,10 @@
  */
  
 #include "general.h"
+#include <libpki/net/pki_mysql.h>
+#include <openssl/md5.h>
+#include <openssl/ripemd.h>
+#include <time.h>
 
 pthread_mutex_t sign_mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -27,6 +31,780 @@
 		NULL
 };
 
+
+struct st_q_param 
+{
+  int           cert_status;
+  ASN1_TIME     *rev_time;
+  unsigned char *p_fingerprint;
+  int           l_fingerprint;
+  unsigned char *cert_type;
+};
+
+static CA_LIST_ENTRY *OCSPD_CA_ENTRY_find(OCSPD_CONFIG *conf, OCSP_CERTID *cid, int *resp_status);
+
+static int PKI_RET_SQL_ERR = -2;
+
+
+static int sk_add_X509_ext(X509_EXTENSION *ex, STACK_OF(X509_EXTENSION) **sk_ex)
+{
+	if(!sk_ex)
+		return(0);
+
+	if(!*sk_ex)
+	{
+		if( (*sk_ex = sk_X509_EXTENSION_new_null() ) == NULL)
+		{
+			PKI_log_err ("sk_X509_EXTENSION_new_null() failed.");
+			return(0);
+		}
+	}
+
+	if(!sk_X509_EXTENSION_push(*sk_ex, X509_EXTENSION_dup(ex)))
+	{
+		PKI_log_err ("sk_X509_EXTENSION_push().");
+		return(0);
+	}
+	return(1);
+}
+
+static int convert_time(size_t l_time, const unsigned char *p_time, ASN1_TIME **rev_time)
+{
+	time_t time = 0;
+	char t_in[20];
+
+
+	if(l_time > sizeof(t_in) - 1)
+	{
+		PKI_log_err("convert_time(): Input time too long: IS: %d - MAX: %d bytes\n", l_time, sizeof(t_in) - 1);
+		return(1);
+	}
+
+	memset(t_in, 0, sizeof(t_in));
+	memcpy(t_in, p_time, l_time);
+
+	PKI_log_debug ( "TIME STR: %s", t_in);
+	if(strlen(t_in) <= 13)
+	{
+		// assuming time string in seconds until the epoch
+		if(strlen(t_in) == 13) // includes also microseconds
+			t_in[10] = 0; // cut off microseconds
+
+		time = (time_t)strtoul(t_in, NULL, 10);
+
+		if(time == 0 || time == ULONG_MAX)
+		{
+			PKI_log_err("convert_time(): Time conversion from '%s' failed", t_in);
+			return(1);
+		}
+
+		PKI_log_debug ( "TIME NUM: %d", time);
+
+		if( (*rev_time = ASN1_TIME_set(NULL, time)) == NULL)
+		{
+			PKI_log_err("convert_time(): ASN1_TIME_set() failed");
+			PKI_log_ossl();
+			return(1);
+		}
+	}
+	else if(strlen(t_in) == 19)
+	{
+		// We assume the following input in t_in: 'YYYY-MM-DD HH:MM:SS'
+
+#if OPENSSL_VERSION_NUMBER > 0x10002000L
+		char tstr[20];
+
+		memset(tstr, 0, sizeof(tstr));
+
+		// We need the following value for conversion with OpenSSL: YYYYMMDDHHMMSSZ
+		memcpy(tstr, t_in, 4);           // YYYY
+		memcpy(tstr + 4, t_in + 5, 2);   // MM
+		memcpy(tstr + 6, t_in + 8, 2);   // DD
+		memcpy(tstr + 8, t_in + 11, 2);  // hh
+		memcpy(tstr + 10, t_in + 14, 2); // mm
+		memcpy(tstr + 12, t_in + 17, 2); // ss
+		tstr[14] = 'Z';
+
+		if( (*rev_time = ASN1_TIME_new()) == NULL)
+		{
+			PKI_log_err("convert_time(): ASN1_TIME_new() failed");
+			PKI_log_ossl();
+			return(1);
+		}
+
+		if(!ASN1_TIME_set_string(*rev_time, tstr))
+		{
+			PKI_log_err("convert_time(): ASN1_TIME_set_string() failed");
+			PKI_log_ossl();
+
+			ASN1_TIME_free(*rev_time);
+			*rev_time = NULL;
+
+			return(1);
+		}
+
+#else
+
+		struct tm tm;
+		time_t    atime;
+		int       nn;
+
+
+		memset(&tm, 0, sizeof(tm));
+
+		 t_in[4] = 0;
+		 t_in[7] = 0;
+		 t_in[10] = 0;
+		 t_in[13] = 0;
+		 t_in[16] = 0;
+
+		// YYYY
+		nn = atoi(t_in);
+		if(nn < 1900)
+		{
+			PKI_log_err("Invalid year: year(%d) < 1900. Exiting\n", nn);
+			return(1);
+		}
+		tm.tm_year = nn - 1900;
+
+		// MM
+		nn = atoi(t_in + 5);
+		if(nn < 1 || nn > 12)
+		{
+			PKI_log_err("Invalid month: 1 > month(%d) > 12. Exiting\n", nn);
+			return(1);
+		}
+		tm.tm_mon = nn - 1;
+		tm.tm_sec = atoi(t_in);
+
+		// DD
+		nn = atoi(t_in + 8);
+		if(nn < 1 || nn > 31)
+		{
+			PKI_log_err("Invalid day: 1 > day(%d) > 31. Exiting\n", nn);
+			return(1);
+		}
+		tm.tm_mday = nn;
+
+		// hh
+		nn = atoi(t_in + 11);
+		if(nn < 0 || nn > 23)
+		{
+			PKI_log_err("Invalid hour: 0 > hour(%d) > 23. Exiting\n", nn);
+			return(1);
+		}
+		tm.tm_hour = nn;
+
+		// mm
+		nn = atoi(t_in + 14);
+		if(nn < 0 || nn > 59)
+		{
+			PKI_log_err("Invalid minute: 0 > minute(%d) > 23. Exiting\n", nn);
+			return(1);
+		}
+		tm.tm_min = nn;
+
+		// ss
+		nn = atoi(t_in + 17);
+		if(nn < 0 || nn > 60)
+		{
+			PKI_log_err("Invalid second: 0 > second(%d) > 60. Exiting\n", nn);
+			return(1);
+		}
+		tm.tm_sec = nn;
+
+		//PKI_log_err("Time: %s\n", asctime(&tm));
+
+		atime = timegm(&tm);
+		if(atime ==  (time_t) -1)
+		{
+			PKI_log_err("timegm() failed: [%d] %s\n", errno, strerror(errno));
+			return(1);
+		}
+
+		if( (*rev_time = ASN1_TIME_set(NULL, atime) ) == NULL)
+		{
+			PKI_log_err("convert_time(): ASN1_TIME_set() failed");
+			PKI_log_ossl();
+			return(1);
+		}
+
+#endif
+
+	}
+	else
+	{
+		PKI_log_err("convert_time(): Unsupported time format");
+		PKI_log_hexdump(PKI_LOG_DEBUG, "Returned time format from SQL query:", (int)l_time, (unsigned char *) p_time);
+
+		return(1);
+	}
+
+	return(0);
+}
+
+//static char *i2h_ASN1_INTEGER(const ASN1_INTEGER *a)
+//{
+//	BIGNUM *bntmp = NULL;
+//	char *strtmp = NULL;
+//
+//	if (!a)
+//        return NULL;
+//	if ((bntmp = ASN1_INTEGER_to_BN(a, NULL)) == NULL
+//		|| (strtmp = BN_bn2hex(bntmp)) == NULL)
+//		PKI_log_err("i2h_ASN1_INTEGER(): Unsupported time format");
+//
+//	BN_free(bntmp);
+//	return strtmp;
+//}
+
+static void print_name(BIO *out, const char *title, X509_NAME *nm, unsigned long lflags)
+{
+  char *buf;
+  char mline = 0;
+  int indent = 0;
+
+  if(title) BIO_puts(out, title);
+  if((lflags & XN_FLAG_SEP_MASK) == XN_FLAG_SEP_MULTILINE) {
+    mline = 1;
+    indent = 4;
+  }
+  if(lflags == XN_FLAG_COMPAT) {
+    buf = X509_NAME_oneline(nm, 0, 0);
+    BIO_puts(out, buf);
+    OPENSSL_free(buf);
+  } else {
+    if(mline) BIO_puts(out, "\n");
+    X509_NAME_print_ex(out, nm, indent, lflags);
+  }
+}
+
+static int create_sql_query(OCSPD_CONFIG  *conf, OCSP_CERTID *cid, CA_LIST_ENTRY *ca, URL *db_url, char **url)
+{
+	int	ret = PKI_ERR;
+	char	*p_data = NULL;
+	BIO	*membio = NULL;
+	long	len = 0;
+
+
+	PKI_log_debug ( "URL path : %s", db_url->path);
+	PKI_log_debug ( "URL attrs: %s", db_url->attrs);
+	PKI_log_debug ( "URL usr  : %s", db_url->usr);
+	//PKI_log_debug ( "URL pwd  : %s", db_url->pwd);
+	PKI_log_debug ( "URL addr : %s", db_url->addr);
+	PKI_log_debug ( "URL port : %d", db_url->port);
+	PKI_log_debug ( "URL orig : %s", db_url->url_s);
+ 
+	if( (membio = BIO_new(BIO_s_mem())) == NULL)
+	{
+	  goto end;
+	}
+
+	if(BIO_printf(membio, "mysql://") <= 0)
+	{
+		PKI_log_err ("BIO_printf(mysql) failed!\n");
+		goto end;
+	}
+
+	if(db_url->usr && db_url->pwd)
+	{
+		if(BIO_printf(membio, "%s:%s@", db_url->usr, db_url->pwd) <= 0)
+		{
+			PKI_log_err ("BIO_printf(user:password) failed!\n");
+			goto end;
+		}
+	}
+
+	if(BIO_printf(membio, "%s:%d/%s/", db_url->addr, db_url->port, db_url->path) <= 0)
+	{
+		PKI_log_err ("BIO_printf(mysql host) failed!\n");
+		goto end;
+	}
+
+	/* dbColumnNameCaFingerprint */
+	if(ca->db_column_ca_fingerprint)
+	{
+		if(BIO_printf(membio, "(%s=%s)",
+				ca->db_column_ca_fingerprint,
+				ca->ca_cert_digest_str) <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameCaFingerprint) failed!\n");
+			goto end;
+		}
+	}
+
+	/* dbColumnNameIssuerNameHash */
+	if(ca->db_column_issuer_name_hash)
+	{
+		if(BIO_printf(membio, "(%s=", ca->db_column_issuer_name_hash) <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameIssuerNameHash) failed!\n");
+			goto end;
+		}
+
+		if(i2a_ASN1_STRING(membio, cid->issuerNameHash, V_ASN1_OCTET_STRING) <= 0)
+		{
+			PKI_log_err ("i2a_ASN1_STRING(issuerNameHash) failed!\n");
+			goto end;
+		}
+
+		if(BIO_printf(membio, ")") <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameIssuerNameHash')') failed!\n");
+			goto end;
+		}
+	}
+
+	/* dbSubjectKeyIdentifier */
+	if(ca->db_column_subject_key_identifier)
+	{
+		if(BIO_printf(membio, "(%s=%s)", ca->db_column_subject_key_identifier, ca->ca_cert_ski) <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbSubjectKeyIdentifier) failed!\n");
+			goto end;
+		}
+	}
+
+	/* dbColumnNameIssuerDN */
+	if(ca->db_column_issuer_dn)
+	{
+		X509_NAME *name = NULL;
+
+		if(BIO_printf(membio, "(%s=", ca->db_column_issuer_dn) <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameIssuerDN) failed!\n");
+			goto end;
+		}
+
+		name = (X509_NAME *)PKI_X509_CERT_get_data(ca->ca_cert, PKI_X509_DATA_SUBJECT);
+		if(!name)
+		{
+			PKI_log_err ("dbColumnNameIssuerDN: No subject name available for given certificate!\n");
+			goto end;
+		}
+
+		(void)print_name(membio, NULL, name, ca->issuer_dn_nmflag);
+
+		if(BIO_printf(membio, ")") <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameIssuerDN')') failed!\n");
+			goto end;
+		}
+	}
+
+	/* dbColumnNameSerialNumber */
+	if(ca->db_column_serial_number)
+	{
+		int  err = 0;
+		char *p_serial = NULL;
+
+		/* convert Serial to a decimal (!) string value */
+		if( (p_serial = PKI_INTEGER_get_parsed(cid->serialNumber) ) == NULL)
+		{
+			PKI_log_err ("i2s_ASN1_INTEGER(serialNumber) failed!\n");
+			goto end;
+		}
+
+		err = BIO_printf(membio, "(%s=%s)", ca->db_column_serial_number, p_serial);
+
+		PKI_Free ( p_serial );
+
+		if( err <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameSerialNumber ) failed!\n");
+			goto end;
+		}
+	}
+
+	/* dbColumnNameCertStatus */
+	if(ca->db_column_cert_status && ca->cert_status_check_value)
+	{
+		if(BIO_printf(membio, "(%s=%s)", ca->db_column_cert_status, ca->cert_status_check_value) <= 0)
+		{
+			PKI_log_err ("BIO_printf(dbColumnNameCertStatus) failed!\n");
+			goto end;
+		}
+	}
+
+	/* Finally add the attributes, that shall be returned */
+	if(db_url->attrs)
+	{
+		if(BIO_printf(membio, "?%s", db_url->attrs) <= 0)
+		{
+			PKI_log_err ("BIO_printf(attrs) failed!\n");
+			goto end;
+		}
+	}
+
+	if(BIO_write(membio, "\x0", 1) <= 0)
+	{
+		PKI_log_err ("BIO_write(0) failed!\n");
+		goto end;
+	}
+
+	if( (len = BIO_get_mem_data(membio, &p_data) ) <= 0)
+	{
+		PKI_log_debug ( "BIO_get_mem_data() failed");
+		goto end;
+	}
+
+	if( conf->debug ) PKI_log_debug ( "%s", p_data);
+
+	if( (*url = PKI_Malloc((size_t) len) ) == NULL)
+	{
+		PKI_log_err ("allocating %d bytes failed\n", len);
+		goto end;
+	}
+
+	memcpy(*url, p_data, (size_t) len);
+
+	ret = PKI_OK;
+
+end:
+	if(membio) BIO_free(membio);
+
+	return(ret);
+}
+
+static int perform_sql_query(OCSPD_CONFIG  *conf, OCSP_CERTID *cid, CA_LIST_ENTRY *ca, char *p_url, struct st_q_param *q_param, long *q_duration)
+{
+	int err = 0;
+	int ret = PKI_ERR;
+	int n_attr = 0;
+	PKI_MEM_STACK *mem_st = NULL;
+	PKI_MEM       *mem    = NULL;
+	LOG_STATS_ITEM_TBL *tbl = NULL;
+	struct timespec tp1, tp2, tp3;
+
+
+	if(!q_param)
+		return(ret);
+
+	// set invalid certificate status
+	q_param->cert_status = -1;
+
+	if( conf->debug ) PKI_log_debug ( "QUERY: %s", p_url);
+
+	if (clock_gettime(CLOCK_MONOTONIC, &tp1) != 0)
+	{
+		PKI_log_err("clock_gettime() failed");
+	}
+
+	mem_st = (PKI_MEM_STACK *)URL_get_data_ex(p_url, ca->db_ctimeout, 0, NULL, &err);
+
+	if (clock_gettime(CLOCK_MONOTONIC, &tp2) != 0)
+	{
+		PKI_log_err("clock_gettime() failed");
+	}
+
+	/* Calculate duration of query in nanoseconds */
+	tp3.tv_sec = tp2.tv_sec - tp1.tv_sec;
+	tp3.tv_nsec = tp2.tv_nsec - tp1.tv_nsec;
+	if (tp3.tv_nsec < 0)
+	{
+		tp3.tv_sec--;
+		tp3.tv_nsec += 1000000000L;
+	}
+	*q_duration = tp3.tv_nsec + tp3.tv_sec * 1000000000L;
+
+	/* NULL is also returned when no rows where found - but also if an DB error (e.g. wrong query) occured */
+	if(mem_st == NULL)
+	{
+		PKI_log_debug ("%s: URL_get_data_ex returned error: %d", __FUNCTION__, err);
+		ret = (err ? err : PKI_RET_SQL_ERR);
+		goto end;
+	}
+
+	// If we get here, we have found something
+
+	if( conf->debug )
+	{
+		PKI_log_debug ( "Number of returned elements: %d", PKI_STACK_MEM_elements(mem_st));
+		PKI_log_debug ( "perform_sql_query(): Number of queried attributes: %d", ca->num_db_attr);
+	}
+
+	// check if only one attribute (field) shall be returned from the SQL query
+	if(ca->num_db_attr > 1)
+	{
+		if(PKI_STACK_MEM_elements(mem_st) != ca->num_db_attr)
+		{
+			ASN1_INTEGER *serial = NULL;
+			char *s = NULL;
+
+			PKI_log_err("perform_sql_query(): Number of queried attributes (%d) does not match number of returned elements (%d)\n",
+				ca->num_db_attr, PKI_STACK_MEM_elements(mem_st));
+
+			OCSP_id_get0_info(NULL, NULL, NULL, &serial, cid);
+			if( (s = PKI_INTEGER_get_parsed ( serial ) ) != NULL)
+			{
+				PKI_log_err("Maybe there is more than one certificate with serial %s, check database!\n", s);
+				PKI_Free ( s );
+			}
+			else
+			{
+				PKI_log_err("Maybe there is more than one certificate with the requested serial, check database!\n");
+			}
+
+			goto end;
+		}
+
+		// Check if the revocation status shall also be retrieved from the database
+		for(tbl = (LOG_STATS_ITEM_TBL *)ocsp_stats_item_tbl; tbl->name; tbl++)
+		{
+			unsigned char *data   = NULL;
+			size_t        l_data  = 0;
+
+      // Preselection
+			switch(ca->db_attr_flags & tbl->flag)
+			{
+				case OCSPD_ATTR_CERT_STATUS:
+					break;
+				case OCSPD_ATTR_REVOCATION_DATE:
+					// If the certificate is not revoked, we can ignore the revocation date
+					if(q_param->cert_status != V_OCSP_CERTSTATUS_REVOKED)
+					{
+						n_attr++; // ignore this attribute
+						continue;
+					}
+				case OCSPD_ATTR_CERT_TYPE:
+				case OCSPD_ATTR_FINGERPRINT:
+					break;
+				default:
+					continue;
+					break;
+			}
+
+			if( (mem = PKI_STACK_MEM_get_num(mem_st, n_attr++) ) == NULL)
+			{
+				if( conf->debug ) PKI_log_debug ( "perform_sql_query():PKI_STACK_MEM_get_num(): no data returned (flag = 0x%x, %s)", tbl->flag, tbl->name);
+				goto end;
+			}
+      
+			if( (data = PKI_MEM_get_data(mem) ) == NULL)
+			{
+				PKI_log_err( "perform_sql_query():PKI_STACK_MEM_get_data(): no data returned (flag = 0x%x, %s)", tbl->flag, tbl->name);
+				goto end;
+			}
+      
+			if( (l_data = PKI_MEM_get_size(mem) ) == 0)
+			{
+				if( conf->debug ) PKI_log_debug ( "perform_sql_query():PKI_STACK_MEM_get_size(): no length returned (flag = 0x%x, %s)", tbl->flag, tbl->name);
+				goto end;
+			}
+        
+			switch(ca->db_attr_flags & tbl->flag)
+			{
+				case OCSPD_ATTR_CERT_STATUS:
+
+					if(n_attr != 1)
+					{
+						PKI_log_err("perform_sql_query(): CertStatus must be the first queried attribute."
+							"Please check ordering of struct ocsp_stats_item_tbl[]\n");
+						goto end;
+					}
+
+					/* [1] <contains the revocation status and will be checked against the revocationCheckValue> */
+					if( conf->debug )
+					{
+						PKI_log_hexdump(PKI_LOG_INFO, "Reference value for revocation check:", (int)l_data, data);
+					}
+        
+					if(ca->revocation_check->negate)
+					{
+						if( conf->debug ) PKI_log_debug("revocationCheckValue: negate check");
+        
+						if(l_data != strlen(ca->revocation_check->value[0]))
+						{
+							PKI_log_debug("revocationCheckValue length mismatch (IS %d bytes - MUST %d bytes)", 
+								l_data, strlen(ca->revocation_check->value[0]));
+            
+							// In this case we assume that the certificate is revoked (negate)
+							q_param->cert_status = V_OCSP_CERTSTATUS_REVOKED;
+						}
+						else if(memcmp(ca->revocation_check->value[0], data, l_data) == 0)
+						{
+							// In this case we assume that certificate is good
+							q_param->cert_status = V_OCSP_CERTSTATUS_GOOD;
+						}
+						else
+						{
+							// length match, values mismatch - certificate should be revoked
+							q_param->cert_status = V_OCSP_CERTSTATUS_REVOKED;
+						}
+					}
+					else
+					{
+						int i;
+        
+						// Set default. If all of the configured values does not match
+						// the certificate is assumed to be valid.
+						q_param->cert_status = V_OCSP_CERTSTATUS_GOOD;
+        
+						for(i = 0; i < ca->revocation_check->nval; i++)
+						{
+							if( conf->debug )
+							{
+								PKI_log_hexdump(PKI_LOG_INFO, "Config value for revocation check:", (int)strlen(ca->revocation_check->value[i]), ca->revocation_check->value[i]);
+							}
+        
+							if(l_data != strlen(ca->revocation_check->value[i]))
+							{
+								PKI_log_debug("revocationCheckValue length mismatch (IS %d bytes - MUST %d bytes)", 
+									l_data, strlen(ca->revocation_check->value[i]));
+								continue;
+							}
+							else if(memcmp(ca->revocation_check->value[i], data, l_data) == 0)
+							{
+								if( conf->debug ) PKI_log_debug("Certificate is revoked");
+								// length match, values match - certificate should be revoked
+								q_param->cert_status = V_OCSP_CERTSTATUS_REVOKED;
+							}
+						}
+					}
+					break;
+
+				case OCSPD_ATTR_REVOCATION_DATE:
+
+					if(n_attr == 1)
+					{
+						PKI_log_err("perform_sql_query(): CertStatus must be the first queried attribute."
+							"Please check ordering of struct ocsp_stats_item_tbl[]\n");
+						goto end;
+					}
+
+					/* <if the certificate is revoked, it contains the revocation date> */
+					// check if the revocation date was also returned
+					if( q_param->cert_status == V_OCSP_CERTSTATUS_REVOKED)
+					{
+						if( conf->debug ) PKI_log_debug ( "perform_sql_query(): Check returned timestamp");
+        
+						if( conf->debug )
+						{
+							PKI_log_hexdump(PKI_LOG_INFO, "perform_sql_query(): Reference value for revocation date:", (int)l_data, data);
+						}
+        
+						if(convert_time(l_data, data, &q_param->rev_time) != 0)
+							goto end;
+					}
+					else if( q_param->cert_status == V_OCSP_CERTSTATUS_REVOKED )
+					{
+						PKI_log_err ( "perform_sql_query():PKI_STACK_MEM_get_num(): no revocation date returned - set status to UNKNOWN");
+						q_param->cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+					}
+					break;
+
+				case OCSPD_ATTR_CERT_TYPE:
+
+					if(n_attr == 1)
+					{
+						PKI_log_err("perform_sql_query(): CertStatus must be the first queried attribute."
+							"Please check ordering of struct ocsp_stats_item_tbl[]\n");
+						goto end;
+					}
+
+					/* <contains the certificate type> */
+					if( conf->debug )
+						PKI_log_hexdump(PKI_LOG_INFO, "CertType:", (int)l_data, data);
+      
+					if( (q_param->cert_type = PKI_Malloc(l_data + 1) ) == NULL)
+					{
+						PKI_log_err("PKI_Malloc(%d bytes) failed\n", l_data + 1);
+						goto end;
+					}
+					memcpy(q_param->cert_type, data, l_data);
+					break;
+
+				case OCSPD_ATTR_FINGERPRINT:
+				{
+					char          *p_data  = NULL;
+        
+					if(n_attr == 1)
+					{
+						PKI_log_err("perform_sql_query(): CertStatus must be the first queried attribute."
+							"Please check ordering of struct ocsp_stats_item_tbl[]\n");
+						goto end;
+					}
+
+					/* <contains the certificate fingerprint> */
+					if( (p_data = PKI_Malloc(l_data + 1) ) == NULL)
+					{
+						PKI_log_err("PKI_Malloc(%d bytes) failed\n", l_data + 1);
+						goto end;
+					}
+        
+					memcpy(p_data, data, l_data);
+        
+#if ( OPENSSL_VERSION_NUMBER >= 0x10000000L )
+					q_param->p_fingerprint = string_to_hex((const char *)p_data, (long *)&q_param->l_fingerprint);
+#else
+					q_param->p_fingerprint = string_to_hex(p_data, (long *)&q_param->l_fingerprint);
+#endif
+        
+					PKI_Free(p_data);
+        
+					if(q_param->p_fingerprint  == NULL)
+					{
+						PKI_log_err(" string_to_hex(fingerprint) failed.\n");
+						goto end;
+					}
+        
+					if(q_param->l_fingerprint != EVP_MD_size(ca->cert_hash_digest) )
+					{
+						PKI_log_err("Certificate fingerprint length differs between DB (%d) and configuration (%d)!\n",
+							q_param->l_fingerprint, EVP_MD_size(ca->cert_hash_digest));
+						goto end;
+					}
+        
+					if( conf->debug ) {
+						PKI_log_hexdump(PKI_LOG_INFO, "Certificate fingerprint (DB):", (int)l_data, data);
+						PKI_log_hexdump(PKI_LOG_INFO, "Certificate fingerprint (bin):", (int)q_param->l_fingerprint, q_param->p_fingerprint);
+					}
+				}
+
+					break;
+				default:
+					PKI_log_debug("Fall through...");
+					break;
+			}
+		}
+	}
+	else
+	{
+		// we have no revocation value to check against, so assuming the certificate is still valid
+		q_param->cert_status = V_OCSP_CERTSTATUS_GOOD;
+	}
+
+	// Perform some sanity checks
+	if( q_param->cert_status == V_OCSP_CERTSTATUS_REVOKED && 
+      q_param->rev_time == NULL)
+	{
+		// we have no revocation value to check against, so assuming the certificate is still valid
+		q_param->cert_status = V_OCSP_CERTSTATUS_GOOD;
+	}
+
+	ret = PKI_OK;
+
+end:
+	PKI_STACK_MEM_free_all(mem_st);
+	if(ret == PKI_ERR) {
+		if(q_param->rev_time) {
+			ASN1_TIME_free(q_param->rev_time);
+			q_param->rev_time = NULL;
+		}
+		if(q_param->p_fingerprint) {
+			PKI_Free(q_param->p_fingerprint);
+			q_param->p_fingerprint = NULL;
+			q_param->l_fingerprint = 0;
+		}
+		if(q_param->cert_type) {
+			PKI_Free(q_param->cert_type);
+			q_param->cert_type = NULL;
+		}
+	}
+
+	return(ret);
+}
+
 int sign_ocsp_response(PKI_X509_OCSP_RESP *resp, OCSPD_CONFIG *conf, PKI_X509_CERT *signCert, 
 		       PKI_X509_CERT *caCert, PKI_TOKEN *tk, PKI_X509_OCSP_RESPID_TYPE resp_id_type)
 {
@@ -72,7 +850,7 @@
 		PKI_log(PKI_LOG_WARNING, "No CA certificate for OCSP response signing");
 
 	// It seems that CISCO devices require the SHA1 algorithm to be
- 	// used. Make sure you use that in the configuration for the digest
+	// used. Make sure you use that in the configuration for the digest
 	if (conf->sigDigest)
 		sign_dgst = conf->sigDigest;
 	else
@@ -122,8 +900,8 @@
 		PKI_log_debug ("Response signed successfully");
 
 	// Test Mode: Issues WRONG signatures by flipping the first
- 	// bit in the signature. Use it ONLY for testing OCSP clients
- 	// verify capabilities!
+	// bit in the signature. Use it ONLY for testing OCSP clients
+	// verify capabilities!
 	if (conf->testmode)
 	{
 		PKI_STRING *signature = NULL;
@@ -136,7 +914,7 @@
 		if (signature)
 		{
 			PKI_X509_OCSP_RESP_VALUE *resp_val = NULL;
-  			PKI_OCSP_RESP *r = NULL;
+			PKI_OCSP_RESP *r = NULL;
 			OCSP_BASICRESP *bsrp = NULL;
 
 			int i = 0;
@@ -145,15 +923,15 @@
 			for (i=0; i < 1; i++ )
 			{
 				if(ASN1_BIT_STRING_get_bit(signature, i))
-    				ASN1_BIT_STRING_set_bit(signature, i, 0);
-    			else
-    				ASN1_BIT_STRING_set_bit(signature, i, 1);
+					ASN1_BIT_STRING_set_bit(signature, i, 0);
+				else
+					ASN1_BIT_STRING_set_bit(signature, i, 1);
 			}
 
 			r = resp->value;
 
 			// Now we need to re-encode the basicresp
-		  	resp_val = r->resp;
+			resp_val = r->resp;
 			bsrp = r->bs;
 
 			if (resp_val->responseBytes)
@@ -162,7 +940,7 @@
 			if (!(resp_val->responseBytes = OCSP_RESPBYTES_new()))
 			{
 				PKI_log_err("Memory Error, aborting signature mangling!");
- 				return PKI_ERR;
+				return PKI_ERR;
 			}
 
 			// Sets the OCSP basic bit
@@ -202,7 +980,62 @@
 	return resp;
 }
 
-PKI_X509_OCSP_RESP *make_ocsp_response(PKI_X509_OCSP_REQ *req, OCSPD_CONFIG *conf )
+static int create_revocation_response(char *parsedSerial, OCSPD_CERT_INFO *cinfo, 
+	X509_REVOKED *entry, OCSPD_CONFIG *conf, CA_LIST_ENTRY *ca,
+	OCSP_CERTID *cid, PKI_TIME *thisupd, PKI_TIME *nextupd,
+	PKI_X509_OCSP_RESP **resp)
+{
+	long reason = -1;
+	void *ext = NULL;
+
+
+	PKI_log(PKI_LOG_INFO, "Status for certificate serial %s is REVOKED", parsedSerial );
+	cinfo->cert_status = V_OCSP_CERTSTATUS_REVOKED;
+
+	// If extensions are found, process them
+	if (entry->extensions)
+	{
+		ASN1_ENUMERATED *asn = NULL;
+
+		if(!(conf->ignore_reason_code || ca->ignore_reason_code) &&
+		    (asn = X509_REVOKED_get_ext_d2i( entry, NID_crl_reason,NULL,NULL )) != NULL )
+		{
+			reason = ASN1_ENUMERATED_get( asn );
+			ASN1_ENUMERATED_free( asn );
+		}
+
+		/* Check and add the invalidity date */
+		ext = X509_REVOKED_get_ext_d2i( entry, NID_invalidity_date, NULL, NULL );
+	}
+
+	if ((PKI_X509_OCSP_RESP_add(*resp, cid, PKI_OCSP_CERTSTATUS_REVOKED,
+			entry->revocationDate, thisupd, nextupd, reason, ext )) == PKI_ERR)
+	{
+		PKI_log_err ("Can not add a simple resp into the OCSP response");
+
+		// Let's free the current response (since there is an internal error,
+		// we do not want to generate partially valid responses)
+		PKI_X509_OCSP_RESP_free(*resp);
+
+		// Generates the error response
+		*resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
+
+		// Skip the processing and return this new response
+		return(PKI_ERR);
+	}
+
+	if( reason == CRL_REASON_CERTIFICATE_HOLD ) {
+		// TODO: We might want to add the CrlID extension to the response
+	}
+
+	if (conf->verbose)
+		PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_REVOKED], parsedSerial);
+
+	return(PKI_OK);
+}
+
+PKI_X509_OCSP_RESP *make_ocsp_response(PKI_X509_OCSP_REQ *req, OCSPD_CONFIG *conf ,
+						OCSPD_SESSION_INFO *sinfo)
 {
 	OCSP_CERTID *cid = NULL;
 
@@ -215,17 +1048,17 @@
 
 	PKI_X509_CERT *signCert = NULL;
 	PKI_X509_CERT *caCert = NULL;
+	X509_EXTENSION *extension = NULL;
 
-	int i, id_count;
+	int i, j, n, id_count;
 	int signResponse;
 
-	int use_server_cert = 0;
-	int use_server_cacert = 0;
-
 	PKI_TIME *thisupd = NULL;
 	PKI_TIME *nextupd = NULL;
 
 	char *parsedSerial = NULL;
+	OCSPD_CERT_INFO *cinfo  = NULL;
+
 
 	// Set the signature bit to 0 (enable only for non-error responses)
 	signResponse = 0;
@@ -241,6 +1074,18 @@
 		goto end;
 	}
 
+	// check for critical extension in OCSP_REQUEST
+	for (j = 0; j < sk_X509_EXTENSION_num(req_val->tbsRequest->requestExtensions); j++)
+	{
+		extension = sk_X509_EXTENSION_value(req_val->tbsRequest->requestExtensions, j);
+		if(X509_EXTENSION_get_critical(extension))
+		{
+			PKI_log_err ( "Critical extenstion found in OCSP requestExtensions\n");
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+			goto end;
+		}
+	}
+
 	// Let's get the number of requests in the OCSP req
 	if ((id_count = OCSP_request_onereq_count(req_val)) <= 0)
 	{
@@ -253,6 +1098,15 @@
 		resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
 		goto end;
 	}
+	if (conf->max_request_count && id_count > conf->max_request_count)
+	{
+		PKI_log_err("Number of requests contained in incoming OCSP request exceeds configured maximum of requests: IS %d - MAX is %d ",
+			id_count, conf->max_request_count);
+
+		// Let's generate the appropriate error response
+		resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+		goto end;
+	}
 
 	// Now allocates the memory for the response
 	if((resp = PKI_X509_OCSP_RESP_new()) == NULL )
@@ -275,61 +1129,203 @@
 	thisupd = PKI_TIME_new(0);
 
 	/* Examine each certificate id in the request */
-	for (i = 0; i < id_count; i++)
+	for (n = 0; n < id_count; n++)
 	{
-		PKI_INTEGER   *serial = NULL;
-		CA_LIST_ENTRY *ca     = NULL;
-		X509_REVOKED  *entry  = NULL;
+		PKI_INTEGER   *serial   = NULL;
+		CA_LIST_ENTRY *ca       = NULL;
+		X509_REVOKED  *entry    = NULL;
+		OCSP_ONEREQ   *one      = NULL;
+		int crl_status = 0;
+		int resp_status = 0;
+
+
+		if( (cinfo = PKI_Malloc(sizeof(OCSPD_CERT_INFO)) ) == NULL)
+		{
+			PKI_log_err("PKI_Malloc(%d bytes) failed\n", sizeof(OCSPD_CERT_INFO));
+			goto end;
+		}
+
+		// push the certificate info immediately on the stack, it will be cleaned up by the calling function
+		if(!sk_void_push(sinfo->sk_cert_info, cinfo))
+		{
+			PKI_log_err("sk_void_push(cinfo, no. %d) failed\n", n);
+			goto end;
+		}
+		cinfo->cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+		cinfo->cached = 0;
+
+		if( (one = OCSP_request_onereq_get0(req_val, n) ) == NULL)
+		{
+			PKI_log_err ( "Extracting one request from input OCSP request\n");
+			if (resp) PKI_X509_OCSP_RESP_free(resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+			goto end;
+		}
+
+		// check for critical extension in OCSP_ONEREQ
+		for (j = 0; j < sk_X509_EXTENSION_num(one->singleRequestExtensions); j++)
+		{
+			extension = sk_X509_EXTENSION_value(one->singleRequestExtensions, j);
+			if(X509_EXTENSION_get_critical(extension))
+			{
+				PKI_log_err ( "Critical extenstion found in OCSP singleRequestExtensions\n" );
+				if (resp) PKI_X509_OCSP_RESP_free (resp);
+				resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+				goto end;
+			}
+		}
+
+		/* Get basic request info */
+		if (((cid = PKI_X509_OCSP_REQ_get_cid(req, n)) == NULL) ||
+				((serial = PKI_X509_OCSP_REQ_get_serial(req, n)) == NULL))
+		{
+			// NO cid found, let's generate a response for a malformed request
+			if (resp) PKI_X509_OCSP_RESP_free(resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+			goto end;
+		}
+
+		if(serial->length <= 0)
+		{
+			PKI_log_err ( "Invalid length of serial number in request: %d\n", serial->length);
+			if (resp) PKI_X509_OCSP_RESP_free (resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+			goto end;
+		}
+
+		if (parsedSerial) PKI_Free(parsedSerial);
+		if( (parsedSerial = PKI_INTEGER_get_parsed(serial)) == NULL)
+		{
+			PKI_log_err ( "Cannot convert serial number in request\n");
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+			goto end;
+		}
+
+		if( (cinfo->serial = BUF_strdup(parsedSerial)) == NULL)
+		{
+			PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL);
+			if (resp) PKI_X509_OCSP_RESP_free(resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
+			goto end;
+		}
+
+		// Some debugging information
+		if (conf->verbose || conf->debug)
+		{
+			PKI_log( PKI_LOG_INFO, "Request for certificate serial %s", parsedSerial);
+		}
+
+		/* Is this request about our CA? */
+		if ((ca = OCSPD_CA_ENTRY_find(conf, cid, &resp_status)) == NULL)
+		{
+			if (conf->verbose)
+				PKI_log(PKI_LOG_INFO, "%s [serial %s]",
+					statusInfo[OCSPD_INFO_NON_RECOGNIZED_CA], parsedSerial);
+
+			if(resp_status)
+			{
+				// if 'resp_status' was set in OCSPD_CA_ENTRY_find() we return an error response
+				if (resp) PKI_X509_OCSP_RESP_free (resp);
+				resp = make_error_response(resp_status);
+				goto end;
+			}
+			else
+			{
+				cinfo->cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+      
+				PKI_log_debug( "After OCSPD_CA_ENTRY_find(): create resp");
+				// Adds the single response to the response container
+				PKI_X509_OCSP_RESP_add(resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN,
+						NULL, NULL, nextupd, 0, NULL);
+      
+				// TODO: Maybe we could add the serviceLocator extension
+				//       we can use the PRQP to find out the server address
+      
+				continue;
+			}
+		}
+
+		crl_status = ca->crl_data->crl_status;
+
+		if(ca->ca_cn)
+		{
+			cinfo->issuer = BUF_strdup(ca->ca_cn);
+
+			if(cinfo->issuer == NULL)
+			{
+				PKI_log_err ("strdup() Memory Allocation error");
+				if (resp) PKI_X509_OCSP_RESP_free (resp);
+				resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
+				goto end;
+			}
+		}
 
-		/* Get basic request info */
-		if (((cid = PKI_X509_OCSP_REQ_get_cid(req, i)) == NULL) ||
-				((serial = PKI_X509_OCSP_REQ_get_serial(req, i)) == NULL))
+		if( (cinfo->ca_id = BUF_strdup(ca->ca_id)) == NULL)
 		{
-			// NO cid found, let's generate a response for a malformed request
-			if (resp) PKI_X509_OCSP_RESP_free(resp);
-			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
-
+			PKI_log_err ("strdup() Memory Allocation error");
+			if (resp) PKI_X509_OCSP_RESP_free (resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
 			goto end;
 		}
 
-		// Some debugging information
-		if (conf->verbose || conf->debug)
+		if(ca->ca_cert_ski && (cinfo->ca_ski = BUF_strdup(ca->ca_cert_ski)) == NULL)
 		{
-			if (parsedSerial) PKI_Free(parsedSerial);
-			parsedSerial = PKI_INTEGER_get_parsed(serial);
-
-			if (conf->debug)
-				PKI_log( PKI_LOG_INFO, "Request for certificate serial %s", parsedSerial);
+			PKI_log_err ("strdup() Memory Allocation error");
+			if (resp) PKI_X509_OCSP_RESP_free (resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
+			goto end;
 		}
 
-		/* Is this request about our CA? */
-		if ((ca = OCSPD_CA_ENTRY_find(conf, cid)) == NULL)
+		if(conf->max_serial)
 		{
-			if (conf->verbose)
-				PKI_log(PKI_LOG_INFO, "%s [serial %s]",
-					statusInfo[OCSPD_INFO_NON_RECOGNIZED_CA], parsedSerial);
+			BIGNUM *bn_serial = NULL;
 
-			// Adds the single response to the response container
-			PKI_X509_OCSP_RESP_add(resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN,
-					NULL, NULL, nextupd, 0, NULL);
+			bn_serial = ASN1_INTEGER_to_BN(serial, NULL);
+			if(!bn_serial)
+			{
+				PKI_log_err ( "Convert ASN1_INTEGER to BIGNUM failed (serial %s)", parsedSerial);
 
-			// TODO: Maybe we could add the serviceLocator extension
- 			//       we can use the PRQP to find out the server address
+				if (resp) PKI_X509_OCSP_RESP_free (resp);
+				resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
+				goto end;
+			}
 
-			continue;
-		}
+			if(BN_is_negative(bn_serial))
+			{
+				PKI_log_err ("Requested serial is negative (%s)", parsedSerial);
+
+				BN_free(bn_serial);
+				if (resp) PKI_X509_OCSP_RESP_free (resp);
+				resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+				goto end;
+			}
+
+			// check if the requested serial is larger then our configured maximum
+			if(BN_cmp(conf->max_serial, bn_serial) == -1)
+			{
+				PKI_log_err ( "Requested serial is too large (serial %s)", parsedSerial);
+
+				BN_free(bn_serial);
+				if (resp) PKI_X509_OCSP_RESP_free (resp);
+				resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+				goto end;
+			}
 
+			BN_free(bn_serial);
+		}
+ 
 		/* If the CA has a specific token, let's use that */
 		if (ca->token != NULL)
 		{
 			tk = ca->token;
 
 			if (conf->debug)
-				PKI_log_debug( "Using the specific token for the found CA (%s)",
-					ca->token_name);
+				PKI_log_debug( "Using the specific token for the found CA (%s)", ca->token_name);
 		}
 		else
 		{
+			PKI_log_debug( "Using the global configured token for the found CA (%s)", conf->token_name);
+
 			// If no specific token but a different server_cert
  			// is to be used, let's report it in debug mode
 			if (ca->server_cert)
@@ -350,11 +1346,11 @@
 		// Here we check for the case where the CRL status is not ok, so
 		// we ask the client to try later, hopefully when we have a valid
 		// CRL to provide the response with
-		if (ca->crl_status != CRL_OK)
+		if ( !(ca->check_issued_by_ca && ca->revocation_check) && crl_status != CRL_OK )
 		{
 			// Check the status of the CRL, if it is not valid, we return a TRY_LATER
 			// or INTERNAL_ERROR responses
-			switch(ca->crl_status)
+			switch(crl_status)
 			{
 				case CRL_ERROR_NEXT_UPDATE:
 					// This situation does not provide any security risk, we can proceed
@@ -367,8 +1363,7 @@
 					if (resp) PKI_X509_OCSP_RESP_free(resp);
 					resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
 					if (conf->debug)
-						PKI_log_debug("sending INTERNAL ERROR (%s)", 
-							get_crl_status_info(ca->crl_status));
+						PKI_log_debug("sending INTERNAL ERROR (%s)", get_crl_status_info(crl_status));
 					goto end;
 					break;
 
@@ -386,7 +1381,7 @@
 						resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_TRYLATER);
 						if (conf->debug)
 							PKI_log_debug("sending TRYLATER (%s)", 
-								get_crl_status_info(ca->crl_status));
+								get_crl_status_info(crl_status));
 						goto end;
 					}
 					break;
@@ -397,8 +1392,8 @@
 					PKI_X509_OCSP_RESP_add(resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN,
 						NULL, NULL, nextupd, CRL_REASON_UNSPECIFIED, NULL);
 					if (conf->debug)
-						PKI_log_debug("setting CERTSTATUS UNKNOWN for serial %s (%s)", 
-							parsedSerial, get_crl_status_info(ca->crl_status));
+						PKI_log_debug("setting CERTSTATUS UNKNOWN for serial %s (%s)",
+							parsedSerial, get_crl_status_info(crl_status));
 					continue;
 			}
 		}
@@ -409,74 +1404,356 @@
 		if (ca->compromised > 0)
 		{
 			PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_REVOKED,
-				NULL, NULL, nextupd, CRL_REASON_CA_COMPROMISE, NULL);
+				ca->compromised_date, NULL, nextupd, CRL_REASON_CA_COMPROMISE, NULL);
+
+			continue;
+		}
+
+		/*
+		 * If configured, set a default certStatus and respond
+		 * immediately without querying database or CRL.
+		 */
+		if (ca->cert_status_default >= 0)
+		{
+			cinfo->cert_status = ca->cert_status_default;
+
+			PKI_log_debug( "Setting configured default certStatus %d", ca->cert_status_default);
+			// Adds the single response to the response container
+			PKI_X509_OCSP_RESP_add(resp, cid, ca->cert_status_default,
+					NULL, NULL, nextupd, 0, NULL);
 
 			continue;
 		}
 
 		// Get the entry from the CRL data, if NULL then the
 		// certificate is not revoked
-		if ((entry = OCSPD_REVOKED_find( ca, serial )) != NULL)
+		if (!(ca->check_issued_by_ca && ca->revocation_check) && (entry = OCSPD_REVOKED_find( ca, serial )) != NULL)
+		{
+			if(create_revocation_response(parsedSerial, cinfo, entry, conf, ca, cid, thisupd, nextupd, &resp) == PKI_ERR)
+				goto end;
+		}
+		else if (ca == NULL )
 		{
-			long reason = -1;
-			void *ext = NULL;
+			if (conf->verbose)
+				PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_NON_RECOGNIZED_CA], parsedSerial);
 
-			// If extensions are found, process them
-			if (entry->extensions)
+			cinfo->cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+
+			PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN, 
+				NULL, thisupd, nextupd, CRL_REASON_UNSPECIFIED, NULL );
+		}
+		else
+		{
+			if(ca->check_issued_by_ca)
 			{
-				ASN1_ENUMERATED *asn = NULL;
+				int ret = 0;
+				char *p_url = NULL;
+				struct st_q_param q_param;
 
-				if( (asn = X509_REVOKED_get_ext_d2i( entry, NID_crl_reason,NULL,NULL )) != NULL )
+
+				memset(&q_param, 0, sizeof(q_param));
+
+				if( (ret = create_sql_query(conf, cid, ca, ca->db_url, &p_url) ) != PKI_OK)
 				{
-					reason = ASN1_ENUMERATED_get( asn );
-					ASN1_ENUMERATED_free( asn );
+					PKI_log_err ("Could not create SQL query!");
+					PKI_X509_OCSP_RESP_set_status ( resp, PKI_X509_OCSP_RESP_STATUS_TRYLATER );
+					PKI_log_err ("SENT TRYLATER (%s)", get_crl_status_info (crl_status));
+
+					goto end;
 				}
+      
+				ret = perform_sql_query(conf, cid, ca, p_url, &q_param, &cinfo->q_duration);
+				PKI_log_debug ("perform_sql_query() returned: %d!", ret);
 
-				/* Check and add the invalidity date */
-				ext = X509_REVOKED_get_ext_d2i( entry, NID_invalidity_date, NULL, NULL );
-			}
+				PKI_Free(p_url);
+				p_url = NULL;
 
-			if ((PKI_X509_OCSP_RESP_add(resp, cid, PKI_OCSP_CERTSTATUS_REVOKED,
-					entry->revocationDate, thisupd, nextupd, reason, ext )) == PKI_ERR)
-			{
-				PKI_log_err ("Can not add a simple resp into the OCSP response");
+#ifndef CR_CONNECTION_ERROR
+  #define CR_CONNECTION_ERROR 2002
+#endif
+#ifndef CR_CONN_HOST_ERROR
+  #define CR_CONN_HOST_ERROR 2003
+#endif
+#ifndef CR_UNKNOWN_HOST
+  #define CR_UNKNOWN_HOST 2005
+#endif
+				switch(ret)
+				{
+					case CR_CONNECTION_ERROR:
+					case CR_CONN_HOST_ERROR:
+					case CR_UNKNOWN_HOST:
+						// If an alternative dbUrl is configured, try again
+						if(ca->db_url2)
+						{
+							PKI_log_debug ("%s: Retry DB connection dbUrl2!", __FUNCTION__);
+							PKI_log_debug ("%s: create_sql_query...!", __FUNCTION__);
+							if( (ret = create_sql_query(conf, cid, ca, ca->db_url2, &p_url) ) != PKI_OK)
+							{
+								PKI_log_err ("Could not create SQL query!");
+								PKI_X509_OCSP_RESP_set_status ( resp, PKI_X509_OCSP_RESP_STATUS_TRYLATER );
+								PKI_log_err ("SENT TRYLATER (%s)", get_crl_status_info (crl_status));
+            
+								goto end;
+							}
+      
+							ret = perform_sql_query(conf, cid, ca, p_url, &q_param, &cinfo->q_duration);
+            
+							PKI_Free(p_url);
+							p_url = NULL;
+						}
+						break;
 
-				// Let's free the current response (since there is an internal error,
-				// we do not want to generate partially valid responses)
-				PKI_X509_OCSP_RESP_free(resp);
+					default:
+						break;
+				}
 
-				// Generates the error response
-				resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_INTERNALERROR);
+				/* certificate not found? */
+				if(ret != PKI_OK)
+				{
+					if(ca->crl_fallback && ret != PKI_ERR && ret != PKI_RET_SQL_ERR)
+					{
+						switch (ret)
+						{
+							case CR_CONNECTION_ERROR:
+							case CR_CONN_HOST_ERROR:
+							case CR_UNKNOWN_HOST:
+								PKI_log_err ("SQL API returned connection error to database. Fallback to CRL!");
+								if ((entry = OCSPD_REVOKED_find( ca, serial )) != NULL)
+								{
+									if(create_revocation_response(parsedSerial, cinfo, entry, conf, ca, cid, thisupd, nextupd, &resp) == PKI_ERR)
+										goto end;
+								}
+								else
+								{
+									if (conf->verbose)
+										PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_VALID_CERT],
+											parsedSerial);
+            
+									cinfo->cert_status = V_OCSP_CERTSTATUS_GOOD;
+            
+									PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_GOOD,
+										NULL, thisupd, nextupd, 0, NULL );
+								}
+								break;
+
+							default:
+								if (resp) PKI_X509_OCSP_RESP_free(resp);
+								resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_TRYLATER);
+								if (conf->debug)
+									PKI_log_debug("SQL error 0x%x - sending TRYLATER", ret); 
+								goto end;
+						}
+					}
+					else
+					{
+						if (ret != PKI_ERR && ret != PKI_RET_SQL_ERR)
+						{
+							if (resp) PKI_X509_OCSP_RESP_free(resp);
+							resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_TRYLATER);
+							if (conf->debug)
+								PKI_log_debug("SQL error 0x%x - sending TRYLATER", ret); 
+							goto end;
+						}
+						else
+						{
+							if (conf->verbose)
+								PKI_log(PKI_LOG_INFO, "%s [serial %s] (not issued by the requested CA)", 
+									statusInfo[OCSPD_INFO_UNKNOWN_STATUS], parsedSerial);
+              
+							cinfo->cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+              
+							if( conf->debug ) PKI_log_debug ( "Certificate not found in SQL database");
+							PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN,
+								NULL, thisupd, nextupd, 0, NULL );
+						}
+					}
+				}
+				else
+				{
+					STACK_OF(X509_EXTENSION) *sk_ex = NULL;
+					X509_EXTENSION *ex = NULL;
 
-				// Skip the processing and return this new response
-				goto end;
-			}
+					PKI_log_debug ( "Certificate found in SQL database");
+
+					if(q_param.cert_type)
+					{
+						cinfo->cert_type = q_param.cert_type;
+						q_param.cert_type = NULL;
+					}
+
+					if(q_param.p_fingerprint)
+					{
+						// create certHash extension
+						ex = PKI_X509_OCSP_CERT_HASH_new(ca->cert_hash_digest, q_param.l_fingerprint, q_param.p_fingerprint);
+						if(ex == NULL)
+						{
+							PKI_log_err ("PKI_X509_OCSP_CERT_HASH_new() failed, set OCSP response status to UNKNOWN.");
+							q_param.cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+						}
+						else
+						{
+							if(!sk_add_X509_ext(ex, &sk_ex))
+							{
+								PKI_log_err ("sk_add_X509_ext(fingerprint) failed, set OCSP response status to UNKNOWN.");
+								q_param.cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+							}
+							X509_EXTENSION_free(ex);
+							ex = NULL;
+						}
+					}
+					else if(ca->db_attr_flags & OCSPD_ATTR_FINGERPRINT) {
+						// certHash is requested but not available, so set status to UNKNOWN
+						PKI_log_err ("certHash requested by configuration, but not available. So set OCSP response status to UNKNOWN.");
+						q_param.cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+					}
+
+					if(ca->archive_cutoff)
+					{
+						// create certHash extension
+						ex = OCSP_archive_cutoff_new(ca->archive_cutoff);
+						if(ex == NULL)
+						{
+							PKI_log_err ("OCSP_archive_cutoff_new() failed, set OCSP response status to UNKNOWN.");
+							q_param.cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+						}
+						else
+						{
+							if(!sk_add_X509_ext(ex, &sk_ex))
+							{
+								PKI_log_err ("sk_add_X509_ext(archiveCutoff) failed, set OCSP response status to UNKNOWN.");
+								q_param.cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+							}
+							X509_EXTENSION_free(ex);
+							ex = NULL;
+						}
+					}
+
+					if(ca->next_update_expired &&
+						(q_param.cert_status == V_OCSP_CERTSTATUS_GOOD || q_param.cert_status == V_OCSP_CERTSTATUS_REVOKED))
+					{
+						ASN1_TIME *t = PKI_X509_CERT_get_data(ca->ca_cert, PKI_X509_DATA_NOTAFTER);
+
+						if(t != NULL)
+						{
+							i = X509_cmp_time(t, NULL);
+							if (i == 0)
+								PKI_log_err ("PKI_TIME_dup(nextUpdateExpired) failed - leaving field empty");
+							else if (i < 0)
+							{
+								if (nextupd) PKI_TIME_free(nextupd);
+								if( (nextupd = PKI_TIME_dup(ca->next_update_expired) ) == NULL)
+									PKI_log_err ("PKI_TIME_dup(nextUpdateExpired) - leaving field empty");
+							}
+						}
+					}
+ 
+					PKI_Free(q_param.p_fingerprint);
+					q_param.p_fingerprint = NULL;
+
+					switch(q_param.cert_status)
+					{
+						case V_OCSP_CERTSTATUS_GOOD:
+							cinfo->cert_status = q_param.cert_status;
+							PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_VALID_CERT], parsedSerial);
+            
+							PKI_X509_OCSP_RESP_add_ex ( resp, cid, PKI_OCSP_CERTSTATUS_GOOD,
+								NULL, thisupd, nextupd, 0, NULL, sk_ex );
+							break;
+
+						case V_OCSP_CERTSTATUS_REVOKED:
+							cinfo->cert_status = q_param.cert_status;
+							PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_REVOKED], parsedSerial);
+            
+							if(!(conf->ignore_reason_code || ca->ignore_reason_code))
+							{
+								PKI_X509_OCSP_RESP_add_ex ( resp, cid, PKI_OCSP_CERTSTATUS_REVOKED,
+									q_param.rev_time, thisupd, nextupd, 0, NULL, sk_ex );
+							}
+							else
+							{
+								PKI_X509_OCSP_RESP_add_ex ( resp, cid, PKI_OCSP_CERTSTATUS_REVOKED,
+									q_param.rev_time, thisupd, nextupd, OCSP_REVOKED_STATUS_NOSTATUS, NULL, sk_ex );
+							}
+
+							ASN1_TIME_free(q_param.rev_time);
+							q_param.rev_time = NULL;
+
+							break;
+
+						case V_OCSP_CERTSTATUS_UNKNOWN:
+						default:
+							if (conf->verbose)
+								PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_UNKNOWN_STATUS], parsedSerial);
+            
+							cinfo->cert_status = V_OCSP_CERTSTATUS_UNKNOWN;
+            
+							PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN,
+								NULL, thisupd, nextupd, 0, NULL );
+
+							ASN1_TIME_free(q_param.rev_time);
+							q_param.rev_time = NULL;
 
-			if( reason == CRL_REASON_CERTIFICATE_HOLD ) {
-				// TODO: We might want to add the CrlID extension to the response
+							break;
+					}
+
+					if(sk_ex)
+					{
+						for(i = 0; i < sk_X509_EXTENSION_num(sk_ex); i++)
+						{
+							X509_EXTENSION *ex = sk_X509_EXTENSION_value(sk_ex, i);
+
+							if(ex)
+								X509_EXTENSION_free(ex);
+						}
+ 
+						sk_X509_EXTENSION_free(sk_ex);
+					}
+				}
 			}
+			else
+			{
+				if (conf->verbose)
+					PKI_log(PKI_LOG_INFO, "%s [serial %s]", statusInfo[OCSPD_INFO_VALID_CERT],
+						parsedSerial);
 
-			if (conf->verbose)
-				PKI_log(PKI_LOG_INFO, "%s [serial %s]",
-					statusInfo[OCSPD_INFO_REVOKED], parsedSerial);
-		}
-		else if (ca == NULL )
-		{
-			if (conf->verbose)
-				PKI_log(PKI_LOG_INFO, "%s [serial %s]",
-						statusInfo[OCSPD_INFO_NON_RECOGNIZED_CA], parsedSerial);
+				cinfo->cert_status = V_OCSP_CERTSTATUS_GOOD;
 
-			PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_UNKNOWN, 
-				NULL, thisupd, nextupd, CRL_REASON_UNSPECIFIED, NULL );
+				PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_GOOD,
+					NULL, thisupd, nextupd, 0, NULL );
+			}
 		}
-		else
+
+		if(!cinfo->cert_type && ca->cert_type)
 		{
-			if (conf->verbose)
-				PKI_log(PKI_LOG_INFO, "%s [serial %s]",
-					statusInfo[OCSPD_INFO_VALID_CERT], parsedSerial);
+			// When the certType could not be retrieved from the DB, set any default (if available)
+			if( (cinfo->cert_type = (unsigned char *) BUF_strdup(ca->cert_type)) == NULL)
+			{
+				PKI_log_err ("BUF_strdup(ca->cert_type) failed - leaving field empty");
+			}
+		}
+	}
+
+	if(conf->max_nonce_length)
+	{
+		X509_EXTENSION *req_ext;
+		int req_idx;
+
+		/* Check for nonce in request */
+		req_idx = OCSP_REQUEST_get_ext_by_NID(req_val, NID_id_pkix_OCSP_Nonce, -1);
 
-			PKI_X509_OCSP_RESP_add ( resp, cid, PKI_OCSP_CERTSTATUS_GOOD,
-				NULL, thisupd, nextupd, 0, NULL );
+		/* If no nonce (req_idx < 0) that's OK */
+		if (req_idx >= 0)
+		{
+			if( (req_ext = OCSP_REQUEST_get_ext(req_val, req_idx) ) != NULL)
+			{
+				if(conf->max_nonce_length < req_ext->value->length)
+				{
+					PKI_log_debug ( "Maximum nonce length exceeded: IS %d bytes MAX %d bytes\n", 
+										req_ext->value->length, conf->max_nonce_length);
+					PKI_X509_OCSP_RESP_set_status (resp, PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST);
+					goto end;
+				}
+			}
 		}
 	}
 
@@ -506,7 +1783,13 @@
 	// Now we need to sign the response
 	if (resp != NULL && signResponse == 1)
 	{
-		if (sign_ocsp_response(resp, conf, signCert, caCert, tk, resp_id_type) != PKI_OK)
+		if (tk == NULL)
+		{
+			// Free the current response, and generate the appropriate error
+			PKI_X509_OCSP_RESP_free(resp);
+			resp = make_error_response(PKI_X509_OCSP_RESP_STATUS_UNAUTHORIZED);
+		}
+		else if (sign_ocsp_response(resp, conf, signCert, caCert, tk, resp_id_type) != PKI_OK)
 		{
 			// Free the current response, and generate the appropriate error
 			PKI_X509_OCSP_RESP_free(resp);
@@ -529,19 +1812,39 @@
 
 	PKI_TIME *date = NULL;
 	PKI_TIME *expire = NULL;
+	PKI_TIME *modified = NULL;
 	PKI_MEM  *mem = NULL;
 
 	char buf[1024];
 	char *tmp_parsed_date = NULL;
 	char *tmp_parsed_expire = NULL;
+	char *tmp_parsed_modified = NULL;
 
 	int buf_size = 0;
 
-	char http_resp[] =
-		"HTTP/1.0 200 OK\r\n"
+	char *http_resp = NULL;
+
+	char http_resp_persistent[] =
+		"HTTP/1.1 200 OK\r\n"
+		"Content-Type: application/ocsp-response\r\n"
+ 		"Content-Transfer-Encoding: Binary\r\n";
+
+	char http_resp_no_persistent[] =
+		"HTTP/1.1 200 OK\r\n"
+		"Connection: close\r\n"
 		"Content-Type: application/ocsp-response\r\n"
  		"Content-Transfer-Encoding: Binary\r\n";
 
+
+	if (conf->http_persistent)
+	{
+		http_resp = http_resp_persistent;
+	}
+	else
+	{
+		http_resp = http_resp_no_persistent;
+	}
+
 	if ( connfd <= 0 )
 	{
 		PKI_log_err("Socket fd is 0!");
@@ -555,66 +1858,95 @@
 		return PKI_ERR;
 	}
 
-	// Gets the time for the expire
-	if (conf->set_nextUpdate)
-		expire = PKI_TIME_new ((conf->nmin*60) + (conf->ndays * 86400));
-	else
-		expire = PKI_TIME_new( 0 );
-
+	/*
+	 * RFC 5019 6.2
+	 *
+	 * This value specifies the date and time at which the
+	 * OCSP responder last modified the response. This date
+	 * and time will be the same as the thisUpdate timestamp
+	 * in the request itself.
+	 */
+	modified = PKI_X509_OCSP_RESP_get_data(r, PKI_X509_DATA_THISUPDATE);
+	if (modified)
+	{
+		char *tmp = PKI_TIME_get_parsed(modified);
+		size_t len = strlen(tmp) + 18; /* Add "Last-Modified: " and "\r\n\0" */
+
+		tmp_parsed_modified = PKI_Malloc(len);
+		snprintf(tmp_parsed_modified, len, "Last-Modified: %s\r\n", tmp);
+		PKI_Free(tmp);
+	}
+
+	/*
+	 * Specifies how long the response is considered fresh.
+	 * This date and time will be the same as the nextUpdate
+	 * timestamp in the OCSP response itself.
+	 */
+	expire = PKI_X509_OCSP_RESP_get_data(r, PKI_X509_DATA_NEXTUPDATE);
 	if (expire)
 	{
-		tmp_parsed_expire = PKI_TIME_get_parsed(expire);
-		PKI_TIME_free(expire);
-		expire = NULL;
+		char *tmp = PKI_TIME_get_parsed(expire);
+		size_t len = strlen(tmp) + 12; /* Add "Expires: " and "\r\n\0" */
+
+		tmp_parsed_expire = PKI_Malloc(len);
+		snprintf(tmp_parsed_expire, len, "Expires: %s\r\n", tmp);
+		PKI_Free(tmp);
+	}
+	else
+	{
+		PKI_TIME *expire2 = NULL;
+
+		/*
+		 * Fall back to config entry or current time
+		 * (for responses which have no nextUpdate)
+		 */
+		if (conf->set_nextUpdate)
+			expire2 = PKI_TIME_new ((conf->nmin*60) + (conf->ndays * 86400));
+		else
+			expire2 = PKI_TIME_new( 0 );
+
+		if (expire2)
+		{
+			char *tmp = PKI_TIME_get_parsed(expire2);
+			PKI_TIME_free(expire2);
+
+			size_t len = strlen(tmp) + 12;
+			tmp_parsed_expire = PKI_Malloc(len);
+			snprintf(tmp_parsed_expire, len, "Expires: %s\r\n", tmp);
+			PKI_Free(tmp);
+		}
 	}
 
-	// Gets current date and time
+	/*
+	 * The date and time at which the OCSP server generated
+	 * the HTTP response.
+	 */
 	if ((date = PKI_TIME_new(0)) != NULL)
 	{
-		tmp_parsed_date = PKI_TIME_get_parsed(date);
+		char *tmp = PKI_TIME_get_parsed(date);
 		PKI_TIME_free(date);
 		date = NULL;
+
+		size_t len = strlen(tmp) + 9; /* Add "Date: " and "\r\n\0" */
+		tmp_parsed_date = PKI_Malloc(len);
+		snprintf(tmp_parsed_date, len, "Date: %s\r\n", tmp);
+		PKI_Free(tmp);
 	}
 
 	// Depending on what we have, we print out the appropriate header
-	if (tmp_parsed_date && tmp_parsed_expire)
-	{
-#if ( LIBPKI_OS_BITS == LIBPKI_OS32)
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %d\r\nDate: %s\r\nExpires: %s\r\n\r\n",
-			http_resp, mem->size, tmp_parsed_date, tmp_parsed_expire);
-#else
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %ld\r\nDate: %s\r\nExpires: %s\r\n\r\n",
-			http_resp, mem->size, tmp_parsed_date, tmp_parsed_expire);
-#endif
-	}
-	else if (tmp_parsed_date)
-	{
-#if ( LIBPKI_OS_BITS == LIBPKI_OS32)
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %d\r\nDate: %s\r\n\r\n",
-			http_resp, mem->size, tmp_parsed_date);
-#else
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %ld\r\nDate: %s\r\n\r\n",
-			http_resp, mem->size, tmp_parsed_date);
-#endif
-	}
-	else if (tmp_parsed_expire)
-	{
 #if ( LIBPKI_OS_BITS == LIBPKI_OS32)
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %d\r\nExpires: %s\r\n\r\n",
-			http_resp, mem->size, tmp_parsed_expire);
-#else
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %ld\r\nExpires: %s\r\n\r\n",
-			http_resp, mem->size, tmp_parsed_expire);
-#endif
-	}
-	else
-	{
-#if ( LIBPKI_OS_BITS == LIBPKI_OS32 )
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %d\r\n\r\n", http_resp, mem->size);
+	buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %d\r\n%s%s%s\r\n",
+		http_resp, mem->size,
+		tmp_parsed_date ? tmp_parsed_date : "",
+		tmp_parsed_expire ? tmp_parsed_expire : "",
+		tmp_parsed_modified ? tmp_parsed_modified : "");
 #else
-		buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %ld\r\n\r\n", http_resp, mem->size);
+	buf_size = snprintf(buf, sizeof(buf), "%sContent-Length: %ld\r\n%s%s%s\r\n",
+		http_resp, mem->size,
+		tmp_parsed_date ? tmp_parsed_date : "",
+		tmp_parsed_expire ? tmp_parsed_expire : "",
+		tmp_parsed_modified ? tmp_parsed_modified : "");
 #endif
-	}
 
 	// Writes the headers to the Network
 	PKI_NET_write(connfd, buf, strlen(buf));
@@ -646,39 +1978,204 @@
 
 	// Frees the memory
 	if (mem) PKI_MEM_free(mem);
+	if (tmp_parsed_modified) PKI_Free(tmp_parsed_modified);
 	if (tmp_parsed_expire) PKI_Free(tmp_parsed_expire);
 	if (tmp_parsed_date) PKI_Free(tmp_parsed_date);
 
 	return PKI_OK;
 }
 
-CA_LIST_ENTRY *OCSPD_CA_ENTRY_find(OCSPD_CONFIG *conf, OCSP_CERTID *cid)
+static CA_LIST_ENTRY *OCSPD_CA_ENTRY_find(OCSPD_CONFIG *conf, OCSP_CERTID *cid, int *resp_status)
 {
 	// STACK_OF(CA_ENTRY_CERTID) *a = NULL;
 
 	int i = 0, ret = PKI_OK;
+	int j;
+	int elements;
+  int err = 0;
 
 	OCSP_CERTID *b = NULL;
 	CA_LIST_ENTRY *ca = NULL;
-	CA_ENTRY_CERTID *tmp = NULL;
+	CA_ENTRY_CERTID *ca_cid = NULL;
+
+	int alg_id1 = 0;
+	int alg_id2 = 0;
+
+	char tmp_buf[128];
+	int tmp_len = sizeof(tmp_buf);
+
+
+	if (cid == NULL)
+	{
+		PKI_log_err("ERROR: missing CertID");
+		*resp_status = PKI_X509_OCSP_RESP_STATUS_INTERNALERROR;
+		return NULL;
+	}
 
 	b = cid;
 
 	if (conf == NULL || conf->ca_list == NULL ) 
 	{
 		PKI_log_err("ERROR: missing conf and/or ca_list");
+		*resp_status = PKI_X509_OCSP_RESP_STATUS_INTERNALERROR;
 		return NULL;
 	}
 
-	int elements = PKI_STACK_elements(conf->ca_list);
+	alg_id1 = OBJ_obj2nid(b->hashAlgorithm->algorithm);
+	if(alg_id1 == NID_undef)
+	{
+		PKI_log_err("ERROR: Cannot get hashAlgorithm from CA CertID");
+		*resp_status = PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST;
+		return NULL;
+	}
+
+	switch(alg_id1)
+	{
+		case NID_md5:
+			if(b->issuerNameHash->length != MD5_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, MD5_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != MD5_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, MD5_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		case NID_sha1:
+			if(b->issuerNameHash->length != SHA_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, SHA_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != SHA_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, SHA_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		case NID_sha224:
+			if(b->issuerNameHash->length != SHA224_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, SHA224_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != SHA224_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, SHA224_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		case NID_sha256:
+			if(b->issuerNameHash->length != SHA256_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, SHA256_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != SHA256_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, SHA256_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		case NID_sha384:
+			if(b->issuerNameHash->length != SHA384_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, SHA384_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != SHA384_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, SHA384_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		case NID_sha512:
+			if(b->issuerNameHash->length != SHA512_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, SHA512_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != SHA512_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, SHA512_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		case NID_ripemd160:
+			if(b->issuerNameHash->length != RIPEMD160_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerNameHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerNameHash->length, RIPEMD160_DIGEST_LENGTH);
+				err = 1;
+			}
+			else if(b->issuerKeyHash->length != RIPEMD160_DIGEST_LENGTH)
+			{
+				PKI_log_err("issuerKeyHash length mismatch (IS %d bytes - MUST %d bytes)", b->issuerKeyHash->length, RIPEMD160_DIGEST_LENGTH);
+				err = 1;
+			}
+			break;
+		default:
+			PKI_log_err("Unsupported digest algorithm for signature creation: %s\n", OBJ_nid2ln(alg_id1));
+			*resp_status = PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST;
+			return NULL;
+	}
+
+	if(err)
+	{
+		*resp_status = PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST;
+		return NULL;
+	}
+ 
+	elements = PKI_STACK_elements(conf->ca_list);
 	for ( i = 0; i < elements; i++ )
 	{
 		ca = (CA_LIST_ENTRY *) PKI_STACK_get_num(conf->ca_list, i);
 
-		tmp = ca->cid;
+		ca_cid = NULL;
+
+		/* check requested hash algorithm for CertID against our CA */
+		for(j = 0; j < sk_CA_ENTRY_CERTID_num(ca->sk_cid); j++)
+		{
+			CA_ENTRY_CERTID *tmp_cid;
+
+			tmp_cid = sk_CA_ENTRY_CERTID_value(ca->sk_cid, j);
+			if(!tmp_cid)
+				continue;
+
+			alg_id2 = OBJ_obj2nid(tmp_cid->hashAlgorithm->algorithm);
+			if(alg_id2 == NID_undef)
+			{
+				PKI_log_err("ERROR: Cannot get hashAlgorithm from CA CertID");
+				return NULL;
+			}
+
+			if (conf->debug) 
+			{
+				if(OBJ_obj2txt(tmp_buf, tmp_len, tmp_cid->hashAlgorithm->algorithm, 0) > 0)
+					PKI_log_debug("OCSPD_CA_ENTRY_find: Check requested CertID algorithm from config: %s", tmp_buf);
+			}
+
+			if(alg_id1 != alg_id2)
+				continue;
+
+			ca_cid = tmp_cid;
+			break;
+		}
+
+		/* no supported hash algorithm found - the supported algorithms are
+		 * equal for every loaded CA - exiting */
+		if(!ca_cid)
+		{
+			if(OBJ_obj2txt(tmp_buf, tmp_len, b->hashAlgorithm->algorithm, 0) > 0)
+				PKI_log_err("ERROR: No supported hash algorithm for requested CertID found (requested algorithm is %s)", tmp_buf);
+			else
+				PKI_log_err("ERROR: No supported hash algorithm for requested CertID found");
+			return NULL;
+		}
 
 		/* Check for hashes */
-		if((ret = ASN1_OCTET_STRING_cmp(tmp->nameHash, b->issuerNameHash)) != 0 )
+		if((ret = ASN1_OCTET_STRING_cmp(ca_cid->nameHash, b->issuerNameHash)) != 0 )
 		{
 			if (conf->debug) 
 			{
@@ -692,7 +2189,7 @@
 			PKI_log_debug("CRL::CA [%s] nameHash OK", ca->ca_id);
 		}
 
-		if ((ret = ASN1_OCTET_STRING_cmp(tmp->keyHash, b->issuerKeyHash)) != 0)
+		if ((ret = ASN1_OCTET_STRING_cmp(ca_cid->keyHash, b->issuerKeyHash)) != 0)
 		{
 			if (conf->debug)
 			{
@@ -725,12 +2222,22 @@
 	int end, cmp_val;
 	int found = 0;
 
+	PKI_RWLOCK_read_lock ( &ca->single_crl_lock );
+
 	/* If no entries are in the list, return directly */
-	if( !(ca) || !(ca->crl) || !(ca->crl_list)) return (r);
+	if( !(ca) || !(ca->crl_data->crl) || !(ca->crl_data->crl_list))
+	{
+		found = 1;
+		goto end;
+	}
  
 	/* Set the end point to the last one */
-	end = sk_X509_REVOKED_num(ca->crl_list) - 1;
-	if( end < 0 ) return (r);
+	end = sk_X509_REVOKED_num(ca->crl_data->crl_list) - 1;
+	if( end < 0 )
+	{
+		found = 1;
+		goto end;
+	}
 
 	while( cont == 1 ) {
 		/* We have not found the entry */
@@ -740,7 +2247,7 @@
 		curr = (int) ((end - start) / 2) + start;
 
 		/* Get the entry from the stack */
-		r = sk_X509_REVOKED_value(ca->crl_list, curr);
+		r = sk_X509_REVOKED_value(ca->crl_data->crl_list, curr);
 
 		/* Compare the two serials */
 		cmp_val = ASN1_INTEGER_cmp(r->serialNumber, serial);
@@ -758,6 +2265,10 @@
 			break;
 		}
 	}
+
+end:
+	PKI_RWLOCK_release_read ( &ca->single_crl_lock );
+
 	if( found )
 		return (r);
 	else
diff -ur openca-ocspd-3.1.2-orig/src/ocspd/threads.c openca-ocspd-3.1.2/src/ocspd/threads.c
--- openca-ocspd-3.1.2-orig/src/ocspd/threads.c	2015-03-26 01:09:15.000000000 +0100
+++ openca-ocspd-3.1.2/src/ocspd/threads.c	2025-11-07 15:17:17.099865042 +0100
@@ -1,11 +1,455 @@
-
+#include <pthread.h>
+#include <sys/time.h>
+#include <libmemcached-1.0/memcached.h>
 #include "general.h"
 
+
 extern OCSPD_CONFIG *ocspd_conf;
+extern const char *x509statusInfo[];
 
 /* Thread Function Prototype */
 void * thread_main ( void *arg );
 
+static const char *responseStatus[] = {
+		"Successful",
+		"MalformedRequest",
+		"InternalError",
+		"tryLater",
+		"n/a",
+		"SigRequired",
+		"Unauthorized"
+};
+
+static const char *certStatus[] = {
+		"GOOD",
+		"REVOKED",
+		"UNKNOWN"
+};
+
+
+#define LOG_FALLBACK 1
+static int log_stats_data(OCSPD_SESSION_INFO *sinfo, OCSPD_CERT_INFO *cinfo, int flags, int db_timeout)
+{
+	int ret = 1;
+	BIO *membio = NULL;
+	PKI_MEM *pki_mem = NULL;
+	long len = 0;
+	char *p_data = NULL;
+	const LOG_STATS_ITEM_TBL *tbl;
+	int first = 1;
+	struct tm tm;
+	char stime[25];
+	char *delimiter = NULL;
+	char prefix[64] = {'\0'};
+
+
+	if( (membio = BIO_new(BIO_s_mem())) == NULL)
+	{
+		PKI_log_err("Memory allocation error!");
+		return(PKI_ERR);
+	}
+
+	if(ocspd_conf->log_stats_url && !flags)
+		delimiter = "','";
+	else
+		delimiter = "; ";
+
+	if(flags)
+		BIO_printf(membio, "LFB: "); // To indicate a log fallback to file
+
+	for(tbl = ocsp_stats_item_tbl; tbl->name; tbl++)
+	{
+		int err = 0;
+
+		if(!ocspd_conf->log_stats_url && !flags)
+			snprintf(prefix, sizeof(prefix), "%s:", tbl->name);
+
+		// For the first item a >'< will be prepended by URL_put_data_url()
+		// After the last item a >'< will be appended by URL_put_data_url()
+		switch(ocspd_conf->log_stats_flags & tbl->flag)
+		{
+			case OCSPD_STATS_INFO_STARTTIME:
+				err = BIO_printf(membio, "%s%s%lld", first ? "" : delimiter, prefix, (long long)(((long long)sinfo->start.tv_sec * 1000) + (long long)sinfo->start.tv_nsec / 1000000));
+				break;
+			case OCSPD_STATS_INFO_ENDTIME:
+				err = BIO_printf(membio, "%s%s%lld", first ? "" : delimiter, prefix, (long long)(((long long)sinfo->stop.tv_sec * 1000) + (long long)sinfo->stop.tv_nsec / 1000000));
+				break;
+			case OCSPD_STATS_ARRIVAL_TIME:
+				// This is for logging an SQL TIMESTAMP with fractional part (support by MySQL 5.6 and above)
+				// http://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-4.html
+				// http://dev.mysql.com/doc/refman/5.6/en/datetime.html
+				if(localtime_r(&sinfo->start.tv_sec, &tm) != NULL)
+				{
+					if(strftime(stime, sizeof(stime), "%Y-%m-%d %H:%M:%S", &tm) > 0)
+						err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, stime);
+						//err = BIO_printf(membio, "%s%s%s%ld", first ? "" : delimiter, prefix, stime, sinfo->start.tv_nsec/1000000);
+					else
+						PKI_log_err("strftime() returnd 0 - unable to calculate arrival time.");
+				}
+				else
+					PKI_log_err("localtime_r() failed - unable to calculate arrival time.");
+				break;
+			case OCSPD_STATS_DEPARTURE_TIME:
+				if(localtime_r(&sinfo->stop.tv_sec, &tm) != NULL)
+				{
+					if(strftime(stime, sizeof(stime), "%Y-%m-%d %H:%M:%S", &tm) > 0)
+						err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, stime);
+						//err = BIO_printf(membio, "%s%s%s%ld", first ? "" : delimiter, prefix, stime, sinfo->stop.tv_nsec/1000000);
+					else
+						PKI_log_err("strftime() returnd 0 - unable to calculate departure time.");
+				}
+				else
+					PKI_log_err("localtime_r() failed - unable to calculate departure time.");
+				break;
+			case OCSPD_STATS_INFO_RESPONSE_STATUS:
+				if(ocspd_conf->log_stats_url && !flags)
+					err = BIO_printf(membio, "%s%s%d", first ? "" : delimiter, prefix, sinfo->resp_status);
+				else
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, responseStatus[sinfo->resp_status]);
+				break;
+			case OCSPD_STATS_INFO_CERT_STATUS:
+				if(ocspd_conf->log_stats_url && !flags)
+					err = BIO_printf(membio, "%s%s%d", first ? "" : delimiter, prefix, cinfo->cert_status);
+				else
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, certStatus[cinfo->cert_status]);
+				break;
+			case OCSPD_STATS_INFO_SERIAL:
+				if(cinfo->serial != NULL)
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, cinfo->serial);
+				else if(flags)
+					err = BIO_printf(membio, "%s%sn/a", first ? "" : delimiter, prefix);
+				else
+					err = BIO_printf(membio, "%s%s-1", first ? "" : delimiter, prefix);
+				break;
+			case OCSPD_STATS_INFO_ISSUER:
+				if(cinfo->issuer != NULL)
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, cinfo->issuer);
+				else
+					err = BIO_printf(membio, "%s%sn/a", first ? "" : delimiter, prefix);
+				break;
+			case OCSPD_STATS_INFO_CANAME:
+				if(cinfo->ca_id != NULL)
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, cinfo->ca_id);
+				else
+					err = BIO_printf(membio, "%s%sn/a", first ? "" : delimiter, prefix);
+				break;
+			case OCSPD_STATS_INFO_SKI:
+				if(cinfo->ca_ski != NULL)
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, cinfo->ca_ski);
+				else
+					err = BIO_printf(membio, "%s%sn/a", first ? "" : delimiter, prefix);
+				break;
+			case OCSPD_STATS_CERT_TYPE:
+				if(cinfo->cert_type != NULL)
+					err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, cinfo->cert_type);
+				else
+					err = BIO_printf(membio, "%s%s", first ? "" : delimiter, prefix);
+				break;
+			case OCSPD_STATS_INFO_IP:
+				err = BIO_printf(membio, "%s%s%s", first ? "" : delimiter, prefix, inet_ntoa(sinfo->cliaddr.sin_addr));
+				break;
+			case OCSPD_STATS_INFO_DURATION:
+				err = BIO_printf(membio, "%s%s%u", first ? "" : delimiter, prefix, sinfo->duration);
+				break;
+			case OCSPD_STATS_RESPONDER_NAME:
+				if(ocspd_conf->responder_name != NULL)
+					err = BIO_printf(membio, "%s%s%s%s", first ? "" : delimiter, prefix, ocspd_conf->responder_name, cinfo->cached ? "c" : "");
+				else
+					err = BIO_printf(membio, "%s%sn/a%s", first ? "" : delimiter, prefix, cinfo->cached ? "c" : "");
+				break;
+			default: // no match
+				continue;
+				break;
+		}
+
+		if(err <= 0)
+		{
+			PKI_log_err ("BIO_printf() failed by flag %X!\n", tbl->flag);
+			goto end;
+		}
+		first = 0;
+	}
+
+	if(ocspd_conf->log_stats_url && !flags)
+	{
+		if( (len = BIO_get_mem_data(membio, &p_data) ) <= 0)
+		{
+			PKI_log_err ( "BIO_get_mem_data() failed");
+			goto end;
+		}
+
+		if ((pki_mem = PKI_MEM_new_data((size_t)len, (unsigned char *)p_data)) == NULL)
+		{
+			PKI_ERROR(PKI_ERR_MEMORY_ALLOC, NULL);
+			goto end;
+		}
+
+		if(URL_put_data_url_ex(ocspd_conf->log_stats_url, pki_mem, NULL, NULL, db_timeout, 0, NULL, NULL) != PKI_OK)
+		{
+			//PKI_log_err ( "URL_put_data_url() failed");
+			// If we fail, call function again and fallback to file logging
+			(void) log_stats_data(sinfo, cinfo, LOG_FALLBACK, 0);
+		}
+		else
+			ret = 0;
+	}
+	else
+	{
+		if(BIO_write(membio, ";\x0", 2) <= 0)
+		{
+			PKI_log_err ("BIO_write() failed!\n");
+			goto end;
+		}
+
+		if( (len = BIO_get_mem_data(membio, &p_data) ) <= 0)
+		{
+			PKI_log_err ( "BIO_get_mem_data() failed");
+			goto end;
+		}
+
+		PKI_log(PKI_LOG_ALWAYS, "%s", p_data);
+		ret = 0;
+	}
+
+end:
+	if(membio)  BIO_free(membio);
+	if(pki_mem) PKI_MEM_free (pki_mem);
+
+	return(ret);
+}
+
+static void log_stats_data_sk(OCSPD_SESSION_INFO *sinfo, int flags, int db_timeout)
+{
+	int i;
+	OCSPD_CERT_INFO *cinfo = NULL;
+
+	for(i = 0; i < sk_void_num(sinfo->sk_cert_info); i++)
+	{
+		if( (cinfo = sk_void_value(sinfo->sk_cert_info, i)) )
+			(void)log_stats_data(sinfo, cinfo , flags, db_timeout);
+	}
+}
+
+
+static void log_timing(OCSPD_SESSION_INFO *sinfo)
+{
+	int i;
+	OCSPD_CERT_INFO *cinfo = NULL;
+
+	for (i = 0; i < sk_void_num(sinfo->sk_cert_info); i++)
+	{
+		if ((cinfo = sk_void_value(sinfo->sk_cert_info, i)))
+		{
+			struct timespec recv, duration;
+
+			/*
+			 * Receive duration - from start of connection until
+			 * the last byte has been received.
+			 */
+			recv.tv_sec = sinfo->start.tv_sec - sinfo->start_recv.tv_sec;
+			recv.tv_nsec = sinfo->start.tv_nsec - sinfo->start_recv.tv_nsec;
+			if (recv.tv_nsec < 0)
+			{
+				recv.tv_sec--;
+				recv.tv_nsec += 1000000000L;
+			}
+
+			/*
+			 * Duration as defined in gemSpec_Perf A_22489-01:
+			 * From reception of last request byte until the start
+			 * of sending the response.
+			 */
+			duration.tv_sec = sinfo->stop.tv_sec - sinfo->start.tv_sec;
+			duration.tv_nsec = sinfo->stop.tv_nsec - sinfo->start.tv_nsec;
+			if (duration.tv_nsec < 0)
+			{
+				duration.tv_sec--;
+				duration.tv_nsec += 1000000000L;
+			}
+
+			/* Output formatted as InfluxDB line protocol */
+			PKI_log(PKI_LOG_ALWAYS, "ocspTiming,ca=\"%s\",sn=\"%s\" responder_name=\"%s%s\","
+				"ip=\"%s\",start_recv=%ld.%09ld,start=%ld.%09ld,stop=%ld.%09ld,duration=%luu,"
+				"duration_recv=%ldu,duration_query=%ldu,unit=\"ns\"",
+				/* tags */
+				cinfo->ca_id ? cinfo->ca_id : "n/a",
+				cinfo->serial ? cinfo->serial : "n/a",
+				/* fields */
+				ocspd_conf->responder_name ? ocspd_conf->responder_name : "n/a",
+				cinfo->cached ? "c" : "",
+				inet_ntoa(sinfo->cliaddr.sin_addr),
+				sinfo->start_recv.tv_sec, sinfo->start_recv.tv_nsec,
+				sinfo->start.tv_sec, sinfo->start.tv_nsec,
+				sinfo->stop.tv_sec, sinfo->stop.tv_nsec,
+				duration.tv_nsec + duration.tv_sec * 1000000000L,
+				recv.tv_nsec + recv.tv_sec * 1000000000L,
+				cinfo->q_duration);
+		}
+	}
+}
+
+
+static PKI_MEM *serialize_response(PKI_X509_OCSP_RESP *resp, OCSPD_SESSION_INFO *sinfo)
+{
+	PKI_MEM *cache_entry = NULL;
+	PKI_MEM *resp_der = NULL;
+	char *resp_mem = NULL;
+	size_t resp_mem_len = 0;
+	int i = 0;
+
+	/*
+	 * Serialization format is size|value|size|value|... starting with
+	 * the actual response, followed by cert info data required for
+	 * the performance log.
+	 */
+
+	/* Get ASN.1/DER encoding of the response and place in cache entry */
+	resp_der = PKI_X509_put_mem(resp, PKI_DATA_FORMAT_ASN1, NULL, NULL);
+	resp_mem = PKI_MEM_get_parsed(resp_der);
+	resp_mem_len = PKI_MEM_get_size(resp_der);
+
+	cache_entry = PKI_MEM_new_data(sizeof resp_mem_len, (unsigned char *)&resp_mem_len);
+	PKI_MEM_add(cache_entry, resp_mem, resp_mem_len);
+
+	PKI_Free(resp_mem);
+	PKI_MEM_free(resp_der);
+
+	/*
+	 * In addition to the actual response we must also cache the cert
+	 * infos that will be used to create the performance log entries.
+	 */
+	for (i = 0; i < sk_void_num(sinfo->sk_cert_info); i++)
+	{
+		OCSPD_CERT_INFO *cinfo = NULL;
+
+		if ((cinfo = sk_void_value(sinfo->sk_cert_info, i)))
+		{
+			size_t cert_statuslen = sizeof cinfo->cert_status;
+			/* Add one to include the strings' null terminator */
+			size_t seriallen = strlen(cinfo->serial) + 1;
+			/* In the event of an error issuer and ca_id might not be set */
+			size_t issuerlen = cinfo->issuer ? strlen(cinfo->issuer) + 1: 0;
+			size_t ca_idlen = cinfo->ca_id ? strlen(cinfo->ca_id) + 1: 0;
+			size_t ca_skilen = cinfo->ca_ski ? strlen(cinfo->ca_ski) + 1: 0;
+			/* cert_type is optional, might not be set */
+			size_t cert_typelen = cinfo->cert_type ? strlen((char *)cinfo->cert_type) + 1 : 0;
+
+			PKI_MEM_add(cache_entry, (char *)&cert_statuslen, sizeof cert_statuslen);
+			PKI_MEM_add(cache_entry, (char *)&cinfo->cert_status, cert_statuslen);
+
+			PKI_MEM_add(cache_entry, (char *)&seriallen, sizeof seriallen);
+			PKI_MEM_add(cache_entry, cinfo->serial, seriallen);
+
+			PKI_MEM_add(cache_entry, (char *)&issuerlen, sizeof issuerlen);
+			PKI_MEM_add(cache_entry, cinfo->issuer, issuerlen);
+
+			PKI_MEM_add(cache_entry, (char *)&ca_idlen, sizeof ca_idlen);
+			PKI_MEM_add(cache_entry, cinfo->ca_id, ca_idlen);
+
+			PKI_MEM_add(cache_entry, (char *)&ca_skilen, sizeof ca_skilen);
+			PKI_MEM_add(cache_entry, cinfo->ca_ski, ca_skilen);
+
+			PKI_MEM_add(cache_entry, (char *)&cert_typelen, sizeof cert_typelen);
+			if (cinfo->cert_type != NULL)
+				PKI_MEM_add(cache_entry, (char *)cinfo->cert_type, cert_typelen);
+		}
+	}
+	return cache_entry;
+}
+
+
+static PKI_X509_OCSP_RESP *deserialize_response(char *cache_entry, size_t cache_entry_len, OCSPD_SESSION_INFO *sinfo)
+{
+	PKI_X509_OCSP_RESP *resp = NULL;
+	PKI_MEM *resp_der = NULL;
+	OCSPD_CERT_INFO *cinfo  = NULL;
+	/* Current element as we move through the cache entry */
+	char *elem = cache_entry;
+	size_t elem_len = 0;
+
+	/* First element is size of the actual response followed by its DER-encoding */
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	resp_der = PKI_MEM_new_data(elem_len, (unsigned char *)elem);
+	resp = PKI_X509_get_mem(resp_der, PKI_DATATYPE_X509_OCSP_RESP, NULL, NULL);
+	PKI_MEM_free(resp_der);
+	elem += elem_len;
+
+	/*
+	 * For some error responses there are no cert infos, so the cache entry
+	 * contains only the response itself.
+	 */
+	if (elem - cache_entry == cache_entry_len)
+	{
+		return resp;
+	}
+
+	if( (cinfo = PKI_Malloc(sizeof (OCSPD_CERT_INFO)) ) == NULL)
+	{
+		PKI_log_err("PKI_Malloc(%d bytes) failed\n", sizeof (OCSPD_CERT_INFO));
+		/* We have no cert infos, but the actual response should still be good */
+		return resp;
+	}    
+
+	// push the certificate info immediately on the stack, it will be cleaned up by the calling function
+	if(!sk_void_push(sinfo->sk_cert_info, cinfo))
+	{    
+		PKI_Free(cinfo);
+		PKI_log_err("sk_void_push(cinfo) failed\n");
+		/* We have no cert infos, but the actual response should still be good */
+		return resp;
+	}     
+
+	/* Fetch the various cert info parts from the cache element */
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	memcpy(&cinfo->cert_status, elem, elem_len);
+	elem += elem_len;
+
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	cinfo->serial = BUF_strdup(elem);
+	elem += elem_len;
+
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	cinfo->issuer = elem_len > 0 ? BUF_strdup(elem) : NULL;
+	elem += elem_len;
+
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	cinfo->ca_id = elem_len > 0 ? BUF_strdup(elem) : NULL;
+	elem += elem_len;
+
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	cinfo->ca_ski = elem_len > 0 ? BUF_strdup(elem) : NULL;
+	elem += elem_len;
+
+	memcpy(&elem_len, elem, sizeof elem_len);
+	elem += sizeof (size_t);
+	cinfo->cert_type = elem_len > 0 ? (unsigned char *)BUF_strdup(elem) : NULL;
+	elem += elem_len;
+
+	cinfo->cached = 1;
+
+	return resp;
+}
+
+
+static void destroy_cache(void *cache)
+{
+	memcached_free(cache);
+}
+
+
+static void cleanup_handler(void *arg)
+{
+	PKI_log_debug ( "Mutex cleanup handler called...");
+
+	PKI_MUTEX_release((PKI_MUTEX *)arg);
+}
+
 
 int thread_make ( int i )
 {
@@ -15,8 +459,6 @@
 	// Basic Memory Check
 	if (!ocspd_conf || !ocspd_conf->threads_list) return -1;
 
-	// Gets the right pointer where to store the new thread identifier
-	th_id = &ocspd_conf->threads_list[i].thread_tid;
 	if ((id = (int *) PKI_Malloc(sizeof(int))) == NULL)
 	{
 		PKI_log_err("Memory allocation error!");
@@ -27,7 +469,6 @@
 	*id = i;
 
 	// Let's generate the new thread
-	// if ((ret = PKI_THREAD_create(th_id, NULL, thread_main, (void *) &i)) != PKI_OK)
 	if ((th_id = PKI_THREAD_new(thread_main, (void *) id)) == NULL)
 	{
 		PKI_log_err("ERROR::OPENCA_SRV_ERR_THREAD_CREATE");
@@ -46,14 +487,43 @@
 
 void * thread_main ( void *arg )
 {
+	int rv = 0;
 	int connfd    = -1;
 	int thread_nr = -1;
 	int *arg_int  = NULL;
+	int ret = PKI_OK;
+	char err_str[128];
+	sigset_t signal_set;
 
-	struct sigaction sa;
+	socklen_t cliaddrlen = sizeof( struct sockaddr_in );
+	socklen_t srvaddrlen = sizeof( struct sockaddr_in );
 
 	PKI_X509_OCSP_REQ  *req = NULL;
 	PKI_X509_OCSP_RESP *resp = NULL;
+	OCSPD_CERT_INFO    *cinfo = NULL;
+	OCSPD_SESSION_INFO sinfo;
+
+	memcached_st *cache = NULL;
+
+	if (ocspd_conf->cache_duration > 0)
+	{
+		cache = memcached(ocspd_conf->cache_settings, strlen(ocspd_conf->cache_settings));
+		if (cache == NULL)
+		{
+			PKI_log(PKI_LOG_ERR, "Failed to set up memcached");
+			ocspd_conf->cache_duration = 0;
+		}
+		else
+		{
+			pthread_key_t key;
+
+			pthread_key_create(&key, destroy_cache);
+			pthread_setspecific(key, cache);
+			PKI_log(PKI_LOG_INFO, "Caching responses for %ld seconds", ocspd_conf->cache_duration);
+		}
+	}
+	else
+		PKI_log(PKI_LOG_INFO, "Cache is disabled");
 
 	if (arg)
 	{
@@ -70,33 +540,50 @@
 	if ( ocspd_conf->verbose )
 		PKI_log(PKI_LOG_INFO, "New Thread Started [%d]", thread_nr);
 
-	// PThread specific SIGPIPE handling
-	sigset_t sigpipe_mask;
-	sigset_t saved_mask;
-
-	// Let's initialize the sigpipe mask
-	sigemptyset(&sigpipe_mask);
+	if(sigemptyset(&signal_set) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigemptyset() failed: [%d] %s\n", errno, err_str);
+		goto exit_thread;
+	}
 
-	// Let's add the SIGPIPE to the mask
-	sigaddset(&sigpipe_mask, SIGPIPE);
+	if(sigaddset(&signal_set, SIGUSR1) == -1)
+	{
+		PKI_strerror ( errno, err_str, sizeof(err_str));
+		PKI_log_err("sigaddset() failed: [%d] %s\n", errno, err_str);
+		goto exit_thread;
+	}
 
-	// Prevent the server to die in case of a write to a prematurely closed socket
-	if (pthread_sigmask(SIG_BLOCK, &sigpipe_mask, &saved_mask) == -1)
+	if( (rv = pthread_sigmask( SIG_UNBLOCK, &signal_set, NULL )) == -1)
 	{
-	  PKI_log_err("Can not block SIGPIPE signal!");
-	  exit(1);
+		PKI_strerror ( rv, err_str, sizeof(err_str));
+		PKI_log_err("pthread_sigmask() failed: [%d] %s\n", errno, err_str);
+		goto exit_thread;
 	}
 
+
 	for ( ; ; )
 	{
+		int count = 1;
+
+		memset(&sinfo, 0, sizeof(sinfo));
+
+		pthread_cleanup_push(cleanup_handler, &ocspd_conf->mutexes[CLIFD_MUTEX]);
+
 		/* Before calling the cond_wait we need to own the mutex */
+		PKI_log_debug ( "worker thread: acquire mutex..");
 		PKI_MUTEX_acquire ( &ocspd_conf->mutexes[CLIFD_MUTEX] );
+		PKI_log_debug ( "worker thread: got mutex..");
+
 		while(ocspd_conf->connfd <= 2)
 		{
+			PKI_log_debug ( "worker thread: cond wait..");
 			PKI_COND_wait ( &ocspd_conf->condVars[CLIFD_COND],
 				&ocspd_conf->mutexes[CLIFD_MUTEX] );
 		}
 
+		PKI_log_debug ( "worker thread: got new connection");
+
 		// Let's copy the socket descriptor
 		connfd = ocspd_conf->connfd;
 
@@ -105,55 +592,263 @@
 
 		// Let's now release the mutex to allow for the server to listen
 		// for the next connection
-		PKI_MUTEX_release ( &ocspd_conf->mutexes[CLIFD_MUTEX] );
+		pthread_cleanup_pop(1);
 
 		// Communicate to the main thread to listen for the next connection
+		pthread_cleanup_push(cleanup_handler, &ocspd_conf->mutexes[SRVFD_MUTEX]);
 		PKI_MUTEX_acquire ( &ocspd_conf->mutexes[SRVFD_MUTEX] );
 		PKI_COND_signal ( &ocspd_conf->condVars[SRVFD_COND] );
-		PKI_MUTEX_release ( &ocspd_conf->mutexes[SRVFD_MUTEX] );
+		pthread_cleanup_pop(1);
 
-		// Retrieves the request from the socket
-		req = ocspd_req_get_socket(connfd, ocspd_conf);
-
-		// If there is an error and we want to debug, let's print some useful info
-		if (req == NULL && ocspd_conf->debug) PKI_log_debug("Can not parse REQ");
+		if (getpeername(connfd, (struct sockaddr*)&sinfo.cliaddr, &cliaddrlen) == -1)
+		{
+			PKI_strerror ( errno, err_str, sizeof(err_str));
+			PKI_log_debug("Network Error [%d::%s] in getpeername", errno, err_str);
+		}
+		if (getsockname(connfd, (struct sockaddr*)&sinfo.srvaddr, &srvaddrlen) == -1)
+		{
+			PKI_strerror ( errno, err_str, sizeof(err_str));
+			PKI_log_debug("Network Error [%d::%s] in getsockname", errno, err_str);
+		}
 
-		// Now let's build the response
-		resp = make_ocsp_response(req, ocspd_conf);
+		PKI_log(PKI_LOG_INFO, "Connection from [%s:%u] to [%s:%u]",
+			inet_ntoa(sinfo.cliaddr.sin_addr), ntohs(sinfo.cliaddr.sin_port),
+			inet_ntoa(sinfo.srvaddr.sin_addr), ntohs(sinfo.srvaddr.sin_port));
 
-		// If we do not have a response, we were not able to generate one
-		// from the received request, let's send a generic error.
-		if (resp == NULL)
+		// Set 'start of receive' timer after connection established but before receiving the whole request
+		if (clock_gettime(CLOCK_REALTIME, &sinfo.start_recv) != 0)
 		{
-			// Error info
-			PKI_log_err("Can not generate the OCSP response (internal error)");
-
-			// Generate the error response
-			resp = PKI_X509_OCSP_RESP_new();
-			if (resp != NULL)
-			{
-				PKI_X509_OCSP_RESP_set_status(resp,
-					PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST );
-			}
+			PKI_strerror(errno, err_str, sizeof err_str);
+			PKI_log_err("Setting start_recv timer with clock_gettime() failed [%d::%s]", errno, err_str);
 		}
 
-end:
+		do {
 
-		// If we have a response, let's send it over the wire and free
-		// the associated memory
-		if (resp != NULL)
-		{
-			// Send the response over the wire
-			ocspd_resp_send_socket( connfd, resp, ocspd_conf );
+			if (ocspd_conf->debug) PKI_log_debug("Process request no. # %d", count++);
 
-			// Frees the response memory
-			PKI_X509_OCSP_RESP_free (resp);
-		}
+			// Retrieves the request from the socket
+			req = ocspd_req_get_socket(connfd, ocspd_conf, &sinfo);
 
-		// Free the memory associated with the request
-		if (req != NULL) PKI_X509_OCSP_REQ_free (req);
+			if (req == NULL)
+			{
+				// If there is an error and we want to debug, let's print some useful info
+				if (ocspd_conf->debug) PKI_log_debug("Can not parse REQ");
+			}
+			else
+			{
+				if( ( sinfo.sk_cert_info = sk_void_new_null() ) == NULL)
+				{
+					PKI_log_err ( "sk_void_new_null() cert_info failed\n");
+					break;
+				}
+
+				/*
+				 * Do not attempt to cache the response if
+				 * - caching is disabled (duration 0)
+				 * - the request contains a nonce
+				 * - the request has more than one element
+				 */
+				if (ocspd_conf->cache_duration == 0 ||
+					PKI_X509_OCSP_REQ_has_nonce(req) ||
+					PKI_X509_OCSP_REQ_elements(req) != 1)
+				{
+					PKI_log(PKI_LOG_INFO, "No caching (disabled or request has nonce / more than one element)");
+
+					// Now let's build the response
+					resp = make_ocsp_response(req, ocspd_conf, &sinfo);
+				}
+				else
+				{
+					memcached_return_t rc = MEMCACHED_FAILURE;
+					char *key = NULL;
+					size_t keylen = 0;
+
+					BUF_MEM *buffer = NULL;
+					BIO *mem = BIO_new(BIO_s_mem());
+					BIO *b64 = BIO_new(BIO_f_base64());
+
+					PKI_log(PKI_LOG_INFO, "Checking cache (single request without nonce)");
+
+					/* Use CERTID as key for the cache */
+					PKI_OCSP_CERTID *cid = PKI_X509_OCSP_REQ_get_cid(req, 0);
+
+					/* Key is a text string, so Base64-encode */
+					mem = BIO_push(b64, mem);
+					BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+					ASN1_i2d_bio_of(OCSP_CERTID, i2d_OCSP_CERTID, mem, cid);
+					BIO_flush(b64);
+					BIO_get_mem_ptr(mem, &buffer);
+					key = BUF_strndup(buffer->data, buffer->length);
+					keylen = buffer->length;
+					BIO_free_all(mem);
+
+					rc = memcached_exist(cache, key, keylen);
+					if (rc == MEMCACHED_SUCCESS)
+					{
+						/* Cache hit */
+						char *cache_entry = NULL;
+						size_t cache_entry_len = 0;
+						uint32_t flags = 0;
+
+						cache_entry = memcached_get(cache, key, keylen, &cache_entry_len, &flags, &rc);
+
+						if (rc == MEMCACHED_SUCCESS && cache_entry != NULL)
+						{
+							resp = deserialize_response(cache_entry, cache_entry_len, &sinfo);
+							free(cache_entry);
+
+							if (resp == NULL)
+								PKI_log_err("Failed PKI_X509_get_mem from memcached");
+						}
+						else
+						{
+							PKI_log_err("memcached_get error %d '%s'", rc, memcached_strerror(cache, rc));
+
+							/* If cache fails build a fresh response */
+							resp = make_ocsp_response(req, ocspd_conf, &sinfo);
+						}
+					}
+					else if (rc == MEMCACHED_NOTFOUND)
+					{
+						/* Cache miss */
+						PKI_MEM *resp_ser = NULL;
+						char *cache_entry = NULL;
+						size_t cache_entry_len = 0;
+						time_t expiration = ocspd_conf->cache_duration;
+						uint32_t flags = 0;
+
+						// Now let's build the response
+						resp = make_ocsp_response(req, ocspd_conf, &sinfo);
+
+						/* Serialize the response and store in cache */
+						resp_ser = serialize_response(resp, &sinfo);
+
+						cache_entry = PKI_MEM_get_parsed(resp_ser);
+						cache_entry_len = PKI_MEM_get_size(resp_ser);
+						PKI_MEM_free(resp_ser);
+
+						memcached_set(cache, key, keylen, cache_entry, (size_t)cache_entry_len, expiration, flags);
+
+						PKI_Free(cache_entry);
+					}
+					else
+					{
+						PKI_log_err("memcached_exist error %d '%s'", rc, memcached_strerror(cache, rc));
+
+						/* If cache fails build a fresh response */
+						resp = make_ocsp_response(req, ocspd_conf, &sinfo);
+					}
+					OPENSSL_free(key);
+				}
+
+				// If we do not have a response, we were not able to generate one
+				// from the received request, let's send a generic error.
+				if (resp == NULL)
+				{
+					// Error info
+					PKI_log_err("Can not generate the OCSP response (internal error)");
+      
+					// Generate the error response
+					resp = PKI_X509_OCSP_RESP_new();
+					if (resp != NULL)
+					{
+						PKI_X509_OCSP_RESP_set_status(resp,
+							PKI_X509_OCSP_RESP_STATUS_MALFORMEDREQUEST );
+					}
+				}
+      
+				// Set end timer before the response is sent
+				if (clock_gettime(CLOCK_REALTIME, &sinfo.stop) != 0)
+				{
+					PKI_strerror ( errno, err_str, sizeof(err_str));
+					PKI_log_err("Setting end timer with clock_gettime() failed [%d::%s]", errno, err_str);
+				}
+
+				// If we have a response, let's send it over the wire and free
+				// the associated memory
+				if (resp != NULL)
+				{
+					PKI_OCSP_RESP * r = resp->value;
+      
+					// Send the response over the wire
+					ocspd_resp_send_socket( connfd, resp, ocspd_conf );
+      
+					sinfo.resp_status = (int)ASN1_ENUMERATED_get(r->resp->responseStatus);
+      
+					// Frees the response memory
+					PKI_X509_OCSP_RESP_free (resp);
+				}
+      
+				// we calculate directly milliseconds for further usage
+				// (round up by adding half a millisecond)
+				sinfo.duration = (unsigned int)((sinfo.stop.tv_sec - sinfo.start.tv_sec) * 1000 + (sinfo.stop.tv_nsec - sinfo.start.tv_nsec + 500000) / 1000000);
+      
+				/* Always report at least 1 millisecond */
+				if (sinfo.duration == 0)
+				{
+					sinfo.duration = 1;
+				}
+
+				if(ocspd_conf->log_stats_flags)
+				{
+					int i;
+					int found = 0;
+      
+					if(ocspd_conf->log_stats_ignore_failed_connections &&
+					   (sinfo.sk_cert_info == NULL || sk_void_num(sinfo.sk_cert_info) < 1))
+					{
+						;
+					}
+					else if(ocspd_conf->ignore_ip_list)
+					{
+						for(i = 0; i < ocspd_conf->ignore_ip_list->nval; i++)
+						{
+							PKI_log_debug("IgnoreIP - Check %s", inet_ntoa(*(ocspd_conf->ignore_ip_list->value[i]))); 
+							if(sinfo.cliaddr.sin_addr.s_addr == ocspd_conf->ignore_ip_list->value[i]->s_addr)
+							{
+								found = 1;
+								break;
+							}
+						}
+     
+						if(!found)
+								(void)log_stats_data_sk(&sinfo, 0, ocspd_conf->db_ctimeout);
+					}
+					else
+						(void)log_stats_data_sk(&sinfo, 0, ocspd_conf->db_ctimeout);
+				}
+				if (ocspd_conf->log_timing)
+				{
+					log_timing(&sinfo);
+				}
+      
+				// Free any existing certificate info
+				while( (cinfo = sk_void_pop(sinfo.sk_cert_info)) )
+				{
+					PKI_Free(cinfo->serial);
+					PKI_Free(cinfo->issuer);
+					PKI_Free(cinfo->ca_id);
+					PKI_Free(cinfo->cert_type);
+					PKI_Free(cinfo->ca_ski);
+					PKI_Free(cinfo);
+				}
+
+				sk_void_free(sinfo.sk_cert_info);
+				sinfo.sk_cert_info = NULL;
+
+				// Free the memory associated with the request
+				PKI_X509_OCSP_REQ_free (req);
+			}
+		} while(ocspd_conf->http_persistent && req != NULL);
 
 		// Finally close the current socket
 		PKI_NET_close(connfd);
 	}
+
+exit_thread:
+	memcached_free(cache);
+	PKI_final_thread();
+	pthread_exit((void*)&ret);
+
+	return(NULL);
 }
openSUSE Build Service is sponsored by