1 /* Dynamic fetching of X.509 CRLs
2 * Copyright (C) 2002 Stephane Laroche <stephane.laroche@colubris.com>
3 * Copyright (C) 2002-2004 Andreas Steffen, Zuercher Hochschule Winterthur
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * RCSID $Id: fetch.c,v 1.11 2005/11/25 10:08:00 as Exp $
29 #include <curl/curl.h>
38 #include "constants.h"
51 fetch_req_t empty_fetch_req
= {
55 { NULL
, 0}, /* issuer */
56 { NULL
, 0}, /* authKeyID */
57 { NULL
, 0}, /* authKeySerialNumber */
58 NULL
/* distributionPoints */
61 /* chained list of crl fetch requests */
62 static fetch_req_t
*crl_fetch_reqs
= NULL
;
64 /* chained list of ocsp fetch requests */
65 static ocsp_location_t
*ocsp_fetch_reqs
= NULL
;
68 static pthread_t thread
;
69 static pthread_mutex_t certs_and_keys_mutex
= PTHREAD_MUTEX_INITIALIZER
;
70 static pthread_mutex_t authcert_list_mutex
= PTHREAD_MUTEX_INITIALIZER
;
71 static pthread_mutex_t crl_list_mutex
= PTHREAD_MUTEX_INITIALIZER
;
72 static pthread_mutex_t ocsp_cache_mutex
= PTHREAD_MUTEX_INITIALIZER
;
73 static pthread_mutex_t ca_info_list_mutex
= PTHREAD_MUTEX_INITIALIZER
;
74 static pthread_mutex_t crl_fetch_list_mutex
= PTHREAD_MUTEX_INITIALIZER
;
75 static pthread_mutex_t ocsp_fetch_list_mutex
= PTHREAD_MUTEX_INITIALIZER
;
76 static pthread_mutex_t fetch_wake_mutex
= PTHREAD_MUTEX_INITIALIZER
;
77 static pthread_cond_t fetch_wake_cond
= PTHREAD_COND_INITIALIZER
;
80 * lock access to my certs and keys
83 lock_certs_and_keys(const char *who
)
85 pthread_mutex_lock(&certs_and_keys_mutex
);
87 DBG_log("certs and keys locked by '%s'", who
)
92 * unlock access to my certs and keys
95 unlock_certs_and_keys(const char *who
)
98 DBG_log("certs and keys unlocked by '%s'", who
)
100 pthread_mutex_unlock(&certs_and_keys_mutex
);
104 * lock access to the chained authcert list
107 lock_authcert_list(const char *who
)
109 pthread_mutex_lock(&authcert_list_mutex
);
111 DBG_log("authcert list locked by '%s'", who
)
116 * unlock access to the chained authcert list
119 unlock_authcert_list(const char *who
)
122 DBG_log("authcert list unlocked by '%s'", who
)
124 pthread_mutex_unlock(&authcert_list_mutex
);
128 * lock access to the chained crl list
131 lock_crl_list(const char *who
)
133 pthread_mutex_lock(&crl_list_mutex
);
135 DBG_log("crl list locked by '%s'", who
)
140 * unlock access to the chained crl list
143 unlock_crl_list(const char *who
)
146 DBG_log("crl list unlocked by '%s'", who
)
148 pthread_mutex_unlock(&crl_list_mutex
);
152 * lock access to the ocsp cache
155 lock_ocsp_cache(const char *who
)
157 pthread_mutex_lock(&ocsp_cache_mutex
);
159 DBG_log("ocsp cache locked by '%s'", who
)
164 * unlock access to the ocsp cache
167 unlock_ocsp_cache(const char *who
)
170 DBG_log("ocsp cache unlocked by '%s'", who
)
172 pthread_mutex_unlock(&ocsp_cache_mutex
);
176 * lock access to the ca info list
179 lock_ca_info_list(const char *who
)
181 pthread_mutex_lock(&ca_info_list_mutex
);
183 DBG_log("ca info list locked by '%s'", who
)
188 * unlock access to the ca info list
191 unlock_ca_info_list(const char *who
)
194 DBG_log("ca info list unlocked by '%s'", who
)
196 pthread_mutex_unlock(&ca_info_list_mutex
);
200 * lock access to the chained crl fetch request list
203 lock_crl_fetch_list(const char *who
)
205 pthread_mutex_lock(&crl_fetch_list_mutex
);
207 DBG_log("crl fetch request list locked by '%s'", who
)
212 * unlock access to the chained crl fetch request list
215 unlock_crl_fetch_list(const char *who
)
218 DBG_log("crl fetch request list unlocked by '%s'", who
)
220 pthread_mutex_unlock(&crl_fetch_list_mutex
);
224 * lock access to the chained ocsp fetch request list
227 lock_ocsp_fetch_list(const char *who
)
229 pthread_mutex_lock(&ocsp_fetch_list_mutex
);
231 DBG_log("ocsp fetch request list locked by '%s'", who
)
236 * unlock access to the chained ocsp fetch request list
239 unlock_ocsp_fetch_list(const char *who
)
242 DBG_log("ocsp fetch request list unlocked by '%s'", who
)
244 pthread_mutex_unlock(&ocsp_fetch_list_mutex
);
248 * wakes up the sleeping fetch thread
251 wake_fetch_thread(const char *who
)
253 if (crl_check_interval
> 0)
256 DBG_log("fetch thread wake call by '%s'", who
)
258 pthread_mutex_lock(&fetch_wake_mutex
);
259 pthread_cond_signal(&fetch_wake_cond
);
260 pthread_mutex_unlock(&fetch_wake_mutex
);
264 #define lock_crl_fetch_list(who) /* do nothing */
265 #define unlock_crl_fetch_list(who) /* do nothing */
266 #define lock_ocsp_fetch_list(who) /* do nothing */
267 #define unlock_ocsp_fetch_list(who) /* do nothing */
268 #endif /* !THREADS */
271 * free the dynamic memory used to store fetch requests
274 free_fetch_request(fetch_req_t
*req
)
276 pfree(req
->issuer
.ptr
);
277 pfreeany(req
->authKeySerialNumber
.ptr
);
278 pfreeany(req
->authKeyID
.ptr
);
279 free_generalNames(req
->distributionPoints
, TRUE
);
283 /* writes data into a dynamically resizeable chunk_t
284 * needed for libcurl responses
287 write_buffer(void *ptr
, size_t size
, size_t nmemb
, void *data
)
289 size_t realsize
= size
* nmemb
;
290 chunk_t
*mem
= (chunk_t
*)data
;
292 mem
->ptr
= (u_char
*)realloc(mem
->ptr
, mem
->len
+ realsize
);
294 memcpy(&(mem
->ptr
[mem
->len
]), ptr
, realsize
);
295 mem
->len
+= realsize
;
302 * fetches a binary blob from a url with libcurl
305 fetch_curl(char *url
, chunk_t
*blob
)
308 char errorbuffer
[CURL_ERROR_SIZE
] = "";
309 chunk_t response
= empty_chunk
;
312 /* get it with libcurl */
313 CURL
*curl
= curl_easy_init();
318 DBG_log("Trying cURL '%s'", url
)
321 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
322 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, write_buffer
);
323 curl_easy_setopt(curl
, CURLOPT_WRITEDATA
, (void *)&response
);
324 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, &errorbuffer
);
325 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, TRUE
);
326 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, FETCH_CMD_TIMEOUT
);
328 res
= curl_easy_perform(curl
);
332 blob
->len
= response
.len
;
333 blob
->ptr
= alloc_bytes(response
.len
, "curl blob");
334 memcpy(blob
->ptr
, response
.ptr
, response
.len
);
338 plog("fetching uri (%s) with libcurl failed: %s", url
, errorbuffer
);
340 curl_easy_cleanup(curl
);
341 /* not using freeanychunk because of realloc (no leak detective) */
344 return strlen(errorbuffer
) > 0 ? "libcurl error" : NULL
;
346 return "warning: not compiled with libcurl support";
347 #endif /* !LIBCURL */
352 * parses the result returned by an ldap query
355 parse_ldap_result(LDAP
* ldap
, LDAPMessage
*result
, chunk_t
*blob
)
359 LDAPMessage
* entry
= ldap_first_entry(ldap
, result
);
363 BerElement
*ber
= NULL
;
366 attr
= ldap_first_attribute(ldap
, entry
, &ber
);
370 struct berval
**values
= ldap_get_values_len(ldap
, entry
, attr
);
374 if (values
[0] != NULL
)
376 blob
->len
= values
[0]->bv_len
;
377 blob
->ptr
= alloc_bytes(blob
->len
, "ldap blob");
378 memcpy(blob
->ptr
, values
[0]->bv_val
, blob
->len
);
379 if (values
[1] != NULL
)
381 plog("warning: more than one value was fetched from LDAP URL");
386 ugh
= "no values in attribute";
388 ldap_value_free_len(values
);
392 ugh
= ldap_err2string(ldap_result2error(ldap
, entry
, 0));
398 ugh
= ldap_err2string(ldap_result2error(ldap
, entry
, 0));
404 ugh
= ldap_err2string(ldap_result2error(ldap
, result
, 0));
410 * fetches a binary blob from an ldap url
413 fetch_ldap_url(char *url
, chunk_t
*blob
)
420 DBG_log("Trying LDAP URL '%s'", url
)
423 rc
= ldap_url_parse(url
, &lurl
);
425 if (rc
== LDAP_SUCCESS
)
427 LDAP
*ldap
= ldap_init(lurl
->lud_host
, lurl
->lud_port
);
431 int ldap_version
= (LDAP_VER
== 2)? LDAP_VERSION2
: LDAP_VERSION3
;
432 struct timeval timeout
;
434 timeout
.tv_sec
= FETCH_CMD_TIMEOUT
;
436 ldap_set_option(ldap
, LDAP_OPT_PROTOCOL_VERSION
, &ldap_version
);
437 ldap_set_option(ldap
, LDAP_OPT_NETWORK_TIMEOUT
, &timeout
);
439 rc
= ldap_simple_bind_s(ldap
, NULL
, NULL
);
441 if (rc
== LDAP_SUCCESS
)
445 timeout
.tv_sec
= FETCH_CMD_TIMEOUT
;
448 rc
= ldap_search_st(ldap
, lurl
->lud_dn
452 , 0, &timeout
, &result
);
454 if (rc
== LDAP_SUCCESS
)
456 ugh
= parse_ldap_result(ldap
, result
, blob
);
457 ldap_msgfree(result
);
461 ugh
= ldap_err2string(rc
);
466 ugh
= ldap_err2string(rc
);
474 ldap_free_urldesc(lurl
);
478 ugh
= ldap_err2string(rc
);
482 #else /* !LDAP_VER */
484 fetch_ldap_url(char *url
, chunk_t
*blob
)
486 return "LDAP URL fetching not activated in pluto source code";
488 #endif /* !LDAP_VER */
491 * fetch an ASN.1 blob coded in PEM or DER format from a URL
494 fetch_asn1_blob(char *url
, chunk_t
*blob
)
498 if (strlen(url
) >= 4 && strncasecmp(url
, "ldap", 4) == 0)
500 ugh
= fetch_ldap_url(url
, blob
);
504 ugh
= fetch_curl(url
, blob
);
512 DBG_log(" fetched blob coded in DER format")
519 ugh
= pemtobin(blob
, NULL
, "", &pgp
);
525 DBG_log(" fetched blob coded in PEM format")
530 ugh
= "blob coded in unknown format";
543 * complete a distributionPoint URI with ca information
546 complete_uri(chunk_t distPoint
, const char *ldaphost
)
549 char *ptr
= distPoint
.ptr
;
550 size_t len
= distPoint
.len
;
552 char *symbol
= memchr(ptr
, ':', len
);
556 size_t type_len
= symbol
- ptr
;
558 if (type_len
>= 4 && strncasecmp(ptr
, "ldap", 4) == 0)
561 len
-= (type_len
+ 1);
563 if (len
> 2 && *ptr
++ == '/' && *ptr
++ == '/')
566 symbol
= memchr(ptr
, '/', len
);
568 if (symbol
!= NULL
&& symbol
- ptr
== 0 && ldaphost
!= NULL
)
570 uri
= alloc_bytes(distPoint
.len
+strlen(ldaphost
)+1, "uri");
572 /* insert the ldaphost into the uri */
573 sprintf(uri
, "%.*s%s%.*s"
574 , (int)(distPoint
.len
- len
), distPoint
.ptr
583 /* default action: copy distributionPoint without change */
584 uri
= alloc_bytes(distPoint
.len
+1, "uri");
585 sprintf(uri
, "%.*s", (int)distPoint
.len
, distPoint
.ptr
);
590 * try to fetch the crls defined by the fetch requests
593 fetch_crls(bool cache_crls
)
598 lock_crl_fetch_list("fetch_crls");
599 req
= crl_fetch_reqs
;
600 reqp
= &crl_fetch_reqs
;
604 bool valid_crl
= FALSE
;
605 chunk_t blob
= empty_chunk
;
606 generalName_t
*gn
= req
->distributionPoints
;
607 const char *ldaphost
;
610 lock_ca_info_list("fetch_crls");
612 ca
= get_ca_info(req
->issuer
, req
->authKeySerialNumber
, req
->authKeyID
);
613 ldaphost
= (ca
== NULL
)? NULL
: ca
->ldaphost
;
617 char *uri
= complete_uri(gn
->name
, ldaphost
);
619 err_t ugh
= fetch_asn1_blob(uri
, &blob
);
624 plog("fetch failed: %s", ugh
);
630 clonetochunk(crl_uri
, gn
->name
.ptr
, gn
->name
.len
, "crl uri");
631 if (insert_crl(blob
, crl_uri
, cache_crls
))
634 DBG_log("we have a valid crl")
643 unlock_ca_info_list("fetch_crls");
647 /* delete fetch request */
648 fetch_req_t
*req_free
= req
;
652 free_fetch_request(req_free
);
656 /* try again next time */
662 unlock_crl_fetch_list("fetch_crls");
666 fetch_ocsp_status(ocsp_location_t
* location
)
670 chunk_t response
= empty_chunk
;
675 request
= build_ocsp_request(location
);
678 DBG_log("sending ocsp request to location '%.*s'"
679 , (int)location
->uri
.len
, location
->uri
.ptr
)
682 DBG_dump_chunk("OCSP request", request
)
685 /* send via http post using libcurl */
686 curl
= curl_easy_init();
690 char errorbuffer
[CURL_ERROR_SIZE
];
691 struct curl_slist
*headers
= NULL
;
692 char* uri
= alloc_bytes(location
->uri
.len
+1, "ocsp uri");
694 /* we need a null terminated string for curl */
695 memcpy(uri
, location
->uri
.ptr
, location
->uri
.len
);
696 *(uri
+ location
->uri
.len
) = '\0';
698 /* set content type header */
699 headers
= curl_slist_append(headers
, "Content-Type: application/ocsp-request");
700 curl_easy_setopt(curl
, CURLOPT_HTTPHEADER
, headers
);
702 curl_easy_setopt(curl
, CURLOPT_URL
, uri
);
703 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, write_buffer
);
704 curl_easy_setopt(curl
, CURLOPT_FILE
, (void *)&response
);
705 curl_easy_setopt(curl
, CURLOPT_POSTFIELDS
, request
.ptr
);
706 curl_easy_setopt(curl
, CURLOPT_POSTFIELDSIZE
, request
.len
);
707 curl_easy_setopt(curl
, CURLOPT_ERRORBUFFER
, &errorbuffer
);
708 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, TRUE
);
709 curl_easy_setopt(curl
, CURLOPT_CONNECTTIMEOUT
, FETCH_CMD_TIMEOUT
);
711 res
= curl_easy_perform(curl
);
716 DBG_log("received ocsp response")
719 DBG_dump_chunk("OCSP response:\n", response
)
721 parse_ocsp(location
, response
);
725 plog("failed to fetch ocsp status from '%s': %s", uri
, errorbuffer
);
727 curl_slist_free_all(headers
);
728 curl_easy_cleanup(curl
);
730 /* not using freeanychunk because of realloc (no leak detective) */
733 freeanychunk(location
->nonce
);
734 freeanychunk(request
);
736 /* increment the trial counter of the unresolved fetch requests */
738 ocsp_certinfo_t
*certinfo
= location
->certinfo
;
740 while (certinfo
!= NULL
)
743 certinfo
= certinfo
->next
;
748 plog("ocsp error: pluto wasn't compiled with libcurl support");
749 #endif /* !LIBCURL */
753 * try to fetch the necessary ocsp information
758 ocsp_location_t
*location
;
760 lock_ocsp_fetch_list("fetch_ocsp");
761 location
= ocsp_fetch_reqs
;
763 /* fetch the ocps status for all locations */
764 while (location
!= NULL
)
766 if (location
->certinfo
!= NULL
)
767 fetch_ocsp_status(location
);
768 location
= location
->next
;
771 unlock_ocsp_fetch_list("fetch_ocsp");
775 fetch_thread(void *arg
)
777 struct timespec wait_interval
;
780 DBG_log("fetch thread started")
783 pthread_mutex_lock(&fetch_wake_mutex
);
789 wait_interval
.tv_nsec
= 0;
790 wait_interval
.tv_sec
= time(NULL
) + crl_check_interval
;
793 DBG_log("next regular crl check in %ld seconds", crl_check_interval
)
795 status
= pthread_cond_timedwait(&fetch_wake_cond
, &fetch_wake_mutex
798 if (status
== ETIMEDOUT
)
802 DBG_log("*time to check crls and the ocsp cache")
810 DBG_log("fetch thread was woken up")
814 fetch_crls(cache_crls
);
820 * initializes curl and starts the fetching thread
829 status
= curl_global_init(CURL_GLOBAL_NOTHING
);
832 plog("libcurl could not be initialized, status = %d", status
);
836 if (crl_check_interval
> 0)
839 status
= pthread_create( &thread
, NULL
, fetch_thread
, NULL
);
842 plog("fetching thread could not be started, status = %d", status
);
845 plog("warning: not compiled with pthread support");
846 #endif /* !THREADS */
853 lock_crl_fetch_list("free_crl_fetch");
855 while (crl_fetch_reqs
!= NULL
)
857 fetch_req_t
*req
= crl_fetch_reqs
;
858 crl_fetch_reqs
= req
->next
;
859 free_fetch_request(req
);
862 unlock_crl_fetch_list("free_crl_fetch");
865 if (crl_check_interval
> 0)
868 curl_global_cleanup();
874 * free the chained list of ocsp requests
877 free_ocsp_fetch(void)
879 lock_ocsp_fetch_list("free_ocsp_fetch");
880 free_ocsp_locations(&ocsp_fetch_reqs
);
881 unlock_ocsp_fetch_list("free_ocsp_fetch");
886 * add additional distribution points
889 add_distribution_points(const generalName_t
*newPoints
,generalName_t
**distributionPoints
)
891 while (newPoints
!= NULL
)
893 /* skip empty distribution point */
894 if (newPoints
->name
.len
> 0)
897 generalName_t
*gn
= *distributionPoints
;
901 if (gn
->kind
== newPoints
->kind
902 && gn
->name
.len
== newPoints
->name
.len
903 && memcmp(gn
->name
.ptr
, newPoints
->name
.ptr
, gn
->name
.len
) == 0)
905 /* skip if the distribution point is already present */
914 /* clone additional distribution point */
915 gn
= clone_thing(*newPoints
, "generalName");
916 clonetochunk(gn
->name
, newPoints
->name
.ptr
, newPoints
->name
.len
919 /* insert additional CRL distribution point */
920 gn
->next
= *distributionPoints
;
921 *distributionPoints
= gn
;
924 newPoints
= newPoints
->next
;
929 build_crl_fetch_request(chunk_t issuer
, chunk_t authKeySerialNumber
930 , chunk_t authKeyID
, const generalName_t
*gn
)
932 fetch_req_t
*req
= alloc_thing(fetch_req_t
, "fetch request");
933 *req
= empty_fetch_req
;
935 /* note current time */
936 req
->installed
= time(NULL
);
939 clonetochunk(req
->issuer
, issuer
.ptr
, issuer
.len
, "issuer");
940 if (authKeySerialNumber
.ptr
!= NULL
)
942 clonetochunk(req
->authKeySerialNumber
, authKeySerialNumber
.ptr
943 , authKeySerialNumber
.len
, "authKeySerialNumber");
945 if (authKeyID
.ptr
!= NULL
)
947 clonetochunk(req
->authKeyID
, authKeyID
.ptr
, authKeyID
.len
, "authKeyID");
950 /* copy distribution points */
951 add_distribution_points(gn
, &req
->distributionPoints
);
957 * add a crl fetch request to the chained list
960 add_crl_fetch_request(fetch_req_t
*req
)
964 lock_crl_fetch_list("add_crl_fetch_request");
969 if ((req
->authKeyID
.ptr
!= NULL
)? same_keyid(req
->authKeyID
, r
->authKeyID
)
970 : (same_dn(req
->issuer
, r
->issuer
)
971 && same_serial(req
->authKeySerialNumber
, r
->authKeySerialNumber
)))
973 /* there is already a fetch request */
975 DBG_log("crl fetch request already exists")
978 /* there might be new distribution points */
979 add_distribution_points(req
->distributionPoints
, &r
->distributionPoints
);
981 unlock_crl_fetch_list("add_crl_fetch_request");
982 free_fetch_request(req
);
988 /* insert new fetch request at the head of the queue */
989 req
->next
= crl_fetch_reqs
;
990 crl_fetch_reqs
= req
;
993 DBG_log("crl fetch request added")
995 unlock_crl_fetch_list("add_crl_fetch_request");
999 * add an ocsp fetch request to the chained list
1002 add_ocsp_fetch_request(ocsp_location_t
*location
, chunk_t serialNumber
)
1004 ocsp_certinfo_t certinfo
;
1006 certinfo
.serialNumber
= serialNumber
;
1008 lock_ocsp_fetch_list("add_ocsp_fetch_request");
1009 add_certinfo(location
, &certinfo
, &ocsp_fetch_reqs
, TRUE
);
1010 unlock_ocsp_fetch_list("add_ocsp_fetch_request");
1014 * list all distribution points
1017 list_distribution_points(const generalName_t
*gn
)
1019 bool first_gn
= TRUE
;
1023 whack_log(RC_COMMENT
, " %s '%.*s'", (first_gn
)? "distPts: "
1024 :" ", (int)gn
->name
.len
, gn
->name
.ptr
);
1031 * list all fetch requests in the chained list
1034 list_crl_fetch_requests(bool utc
)
1038 lock_crl_fetch_list("list_crl_fetch_requests");
1039 req
= crl_fetch_reqs
;
1043 whack_log(RC_COMMENT
, " ");
1044 whack_log(RC_COMMENT
, "List of CRL fetch requests:");
1045 whack_log(RC_COMMENT
, " ");
1050 u_char buf
[BUF_LEN
];
1052 whack_log(RC_COMMENT
, "%s, trials: %d"
1053 , timetoa(&req
->installed
, utc
), req
->trials
);
1054 dntoa(buf
, BUF_LEN
, req
->issuer
);
1055 whack_log(RC_COMMENT
, " issuer: '%s'", buf
);
1056 if (req
->authKeyID
.ptr
!= NULL
)
1058 datatot(req
->authKeyID
.ptr
, req
->authKeyID
.len
, ':'
1060 whack_log(RC_COMMENT
, " authkey: %s", buf
);
1062 if (req
->authKeySerialNumber
.ptr
!= NULL
)
1064 datatot(req
->authKeySerialNumber
.ptr
, req
->authKeySerialNumber
.len
, ':'
1066 whack_log(RC_COMMENT
, " aserial: %s", buf
);
1068 list_distribution_points(req
->distributionPoints
);
1071 unlock_crl_fetch_list("list_crl_fetch_requests");
1075 list_ocsp_fetch_requests(bool utc
)
1077 lock_ocsp_fetch_list("list_ocsp_fetch_requests");
1078 list_ocsp_locations(ocsp_fetch_reqs
, TRUE
, utc
, FALSE
);
1079 unlock_ocsp_fetch_list("list_ocsp_fetch_requests");