sl@0: /* ocsp_cl.c */ sl@0: /* Written by Tom Titchener for the OpenSSL sl@0: * project. */ sl@0: sl@0: /* History: sl@0: This file was transfered to Richard Levitte from CertCo by Kathy sl@0: Weinhold in mid-spring 2000 to be included in OpenSSL or released sl@0: as a patch kit. */ sl@0: sl@0: /* ==================================================================== sl@0: * Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in sl@0: * the documentation and/or other materials provided with the sl@0: * distribution. sl@0: * sl@0: * 3. All advertising materials mentioning features or use of this sl@0: * software must display the following acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" sl@0: * sl@0: * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to sl@0: * endorse or promote products derived from this software without sl@0: * prior written permission. For written permission, please contact sl@0: * openssl-core@openssl.org. sl@0: * sl@0: * 5. Products derived from this software may not be called "OpenSSL" sl@0: * nor may "OpenSSL" appear in their names without prior written sl@0: * permission of the OpenSSL Project. sl@0: * sl@0: * 6. Redistributions of any form whatsoever must retain the following sl@0: * acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit (http://www.openssl.org/)" sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY sl@0: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sl@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR sl@0: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, sl@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT sl@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; sl@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, sl@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) sl@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED sl@0: * OF THE POSSIBILITY OF SUCH DAMAGE. sl@0: * ==================================================================== sl@0: * sl@0: * This product includes cryptographic software written by Eric Young sl@0: * (eay@cryptsoft.com). This product includes software written by Tim sl@0: * Hudson (tjh@cryptsoft.com). sl@0: * sl@0: */ sl@0: sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: #include sl@0: sl@0: /* Utility functions related to sending OCSP requests and extracting sl@0: * relevant information from the response. sl@0: */ sl@0: sl@0: /* Add an OCSP_CERTID to an OCSP request. Return new OCSP_ONEREQ sl@0: * pointer: useful if we want to add extensions. sl@0: */ sl@0: sl@0: EXPORT_C OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) sl@0: { sl@0: OCSP_ONEREQ *one = NULL; sl@0: sl@0: if (!(one = OCSP_ONEREQ_new())) goto err; sl@0: if (one->reqCert) OCSP_CERTID_free(one->reqCert); sl@0: one->reqCert = cid; sl@0: if (req && sl@0: !sk_OCSP_ONEREQ_push(req->tbsRequest->requestList, one)) sl@0: goto err; sl@0: return one; sl@0: err: sl@0: OCSP_ONEREQ_free(one); sl@0: return NULL; sl@0: } sl@0: sl@0: /* Set requestorName from an X509_NAME structure */ sl@0: sl@0: EXPORT_C int OCSP_request_set1_name(OCSP_REQUEST *req, X509_NAME *nm) sl@0: { sl@0: GENERAL_NAME *gen; sl@0: gen = GENERAL_NAME_new(); sl@0: if (gen == NULL) sl@0: return 0; sl@0: if (!X509_NAME_set(&gen->d.directoryName, nm)) sl@0: { sl@0: GENERAL_NAME_free(gen); sl@0: return 0; sl@0: } sl@0: gen->type = GEN_DIRNAME; sl@0: if (req->tbsRequest->requestorName) sl@0: GENERAL_NAME_free(req->tbsRequest->requestorName); sl@0: req->tbsRequest->requestorName = gen; sl@0: return 1; sl@0: } sl@0: sl@0: sl@0: /* Add a certificate to an OCSP request */ sl@0: sl@0: EXPORT_C int OCSP_request_add1_cert(OCSP_REQUEST *req, X509 *cert) sl@0: { sl@0: OCSP_SIGNATURE *sig; sl@0: if (!req->optionalSignature) sl@0: req->optionalSignature = OCSP_SIGNATURE_new(); sl@0: sig = req->optionalSignature; sl@0: if (!sig) return 0; sl@0: if (!cert) return 1; sl@0: if (!sig->certs && !(sig->certs = sk_X509_new_null())) sl@0: return 0; sl@0: sl@0: if(!sk_X509_push(sig->certs, cert)) return 0; sl@0: CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); sl@0: return 1; sl@0: } sl@0: sl@0: /* Sign an OCSP request set the requestorName to the subjec sl@0: * name of an optional signers certificate and include one sl@0: * or more optional certificates in the request. Behaves sl@0: * like PKCS7_sign(). sl@0: */ sl@0: sl@0: EXPORT_C int OCSP_request_sign(OCSP_REQUEST *req, sl@0: X509 *signer, sl@0: EVP_PKEY *key, sl@0: const EVP_MD *dgst, sl@0: STACK_OF(X509) *certs, sl@0: unsigned long flags) sl@0: { sl@0: int i; sl@0: OCSP_SIGNATURE *sig; sl@0: X509 *x; sl@0: sl@0: if (!OCSP_request_set1_name(req, X509_get_subject_name(signer))) sl@0: goto err; sl@0: sl@0: if (!(req->optionalSignature = sig = OCSP_SIGNATURE_new())) goto err; sl@0: if (!dgst) dgst = EVP_sha1(); sl@0: if (key) sl@0: { sl@0: if (!X509_check_private_key(signer, key)) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_REQUEST_SIGN, OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE); sl@0: goto err; sl@0: } sl@0: if (!OCSP_REQUEST_sign(req, key, dgst)) goto err; sl@0: } sl@0: sl@0: if (!(flags & OCSP_NOCERTS)) sl@0: { sl@0: if(!OCSP_request_add1_cert(req, signer)) goto err; sl@0: for (i = 0; i < sk_X509_num(certs); i++) sl@0: { sl@0: x = sk_X509_value(certs, i); sl@0: if (!OCSP_request_add1_cert(req, x)) goto err; sl@0: } sl@0: } sl@0: sl@0: return 1; sl@0: err: sl@0: OCSP_SIGNATURE_free(req->optionalSignature); sl@0: req->optionalSignature = NULL; sl@0: return 0; sl@0: } sl@0: sl@0: /* Get response status */ sl@0: sl@0: EXPORT_C int OCSP_response_status(OCSP_RESPONSE *resp) sl@0: { sl@0: return ASN1_ENUMERATED_get(resp->responseStatus); sl@0: } sl@0: sl@0: /* Extract basic response from OCSP_RESPONSE or NULL if sl@0: * no basic response present. sl@0: */ sl@0: sl@0: sl@0: EXPORT_C OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) sl@0: { sl@0: OCSP_RESPBYTES *rb; sl@0: rb = resp->responseBytes; sl@0: if (!rb) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_RESPONSE_GET1_BASIC, OCSP_R_NO_RESPONSE_DATA); sl@0: return NULL; sl@0: } sl@0: if (OBJ_obj2nid(rb->responseType) != NID_id_pkix_OCSP_basic) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_RESPONSE_GET1_BASIC, OCSP_R_NOT_BASIC_RESPONSE); sl@0: return NULL; sl@0: } sl@0: sl@0: return ASN1_item_unpack(rb->response, ASN1_ITEM_rptr(OCSP_BASICRESP)); sl@0: } sl@0: sl@0: /* Return number of OCSP_SINGLERESP reponses present in sl@0: * a basic response. sl@0: */ sl@0: sl@0: EXPORT_C int OCSP_resp_count(OCSP_BASICRESP *bs) sl@0: { sl@0: if (!bs) return -1; sl@0: return sk_OCSP_SINGLERESP_num(bs->tbsResponseData->responses); sl@0: } sl@0: sl@0: /* Extract an OCSP_SINGLERESP response with a given index */ sl@0: sl@0: EXPORT_C OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx) sl@0: { sl@0: if (!bs) return NULL; sl@0: return sk_OCSP_SINGLERESP_value(bs->tbsResponseData->responses, idx); sl@0: } sl@0: sl@0: /* Look single response matching a given certificate ID */ sl@0: sl@0: EXPORT_C int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) sl@0: { sl@0: int i; sl@0: STACK_OF(OCSP_SINGLERESP) *sresp; sl@0: OCSP_SINGLERESP *single; sl@0: if (!bs) return -1; sl@0: if (last < 0) last = 0; sl@0: else last++; sl@0: sresp = bs->tbsResponseData->responses; sl@0: for (i = last; i < sk_OCSP_SINGLERESP_num(sresp); i++) sl@0: { sl@0: single = sk_OCSP_SINGLERESP_value(sresp, i); sl@0: if (!OCSP_id_cmp(id, single->certId)) return i; sl@0: } sl@0: return -1; sl@0: } sl@0: sl@0: /* Extract status information from an OCSP_SINGLERESP structure. sl@0: * Note: the revtime and reason values are only set if the sl@0: * certificate status is revoked. Returns numerical value of sl@0: * status. sl@0: */ sl@0: sl@0: EXPORT_C int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, sl@0: ASN1_GENERALIZEDTIME **revtime, sl@0: ASN1_GENERALIZEDTIME **thisupd, sl@0: ASN1_GENERALIZEDTIME **nextupd) sl@0: { sl@0: int ret; sl@0: OCSP_CERTSTATUS *cst; sl@0: if(!single) return -1; sl@0: cst = single->certStatus; sl@0: ret = cst->type; sl@0: if (ret == V_OCSP_CERTSTATUS_REVOKED) sl@0: { sl@0: OCSP_REVOKEDINFO *rev = cst->value.revoked; sl@0: if (revtime) *revtime = rev->revocationTime; sl@0: if (reason) sl@0: { sl@0: if(rev->revocationReason) sl@0: *reason = ASN1_ENUMERATED_get(rev->revocationReason); sl@0: else *reason = -1; sl@0: } sl@0: } sl@0: if(thisupd) *thisupd = single->thisUpdate; sl@0: if(nextupd) *nextupd = single->nextUpdate; sl@0: return ret; sl@0: } sl@0: sl@0: /* This function combines the previous ones: look up a certificate ID and sl@0: * if found extract status information. Return 0 is successful. sl@0: */ sl@0: sl@0: EXPORT_C int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status, sl@0: int *reason, sl@0: ASN1_GENERALIZEDTIME **revtime, sl@0: ASN1_GENERALIZEDTIME **thisupd, sl@0: ASN1_GENERALIZEDTIME **nextupd) sl@0: { sl@0: int i; sl@0: OCSP_SINGLERESP *single; sl@0: i = OCSP_resp_find(bs, id, -1); sl@0: /* Maybe check for multiple responses and give an error? */ sl@0: if(i < 0) return 0; sl@0: single = OCSP_resp_get0(bs, i); sl@0: i = OCSP_single_get0_status(single, reason, revtime, thisupd, nextupd); sl@0: if(status) *status = i; sl@0: return 1; sl@0: } sl@0: sl@0: /* Check validity of thisUpdate and nextUpdate fields. It is possible that the request will sl@0: * take a few seconds to process and/or the time wont be totally accurate. Therefore to avoid sl@0: * rejecting otherwise valid time we allow the times to be within 'nsec' of the current time. sl@0: * Also to avoid accepting very old responses without a nextUpdate field an optional maxage sl@0: * parameter specifies the maximum age the thisUpdate field can be. sl@0: */ sl@0: sl@0: EXPORT_C int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long nsec, long maxsec) sl@0: { sl@0: int ret = 1; sl@0: time_t t_now, t_tmp; sl@0: time(&t_now); sl@0: /* Check thisUpdate is valid and not more than nsec in the future */ sl@0: if (!ASN1_GENERALIZEDTIME_check(thisupd)) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_ERROR_IN_THISUPDATE_FIELD); sl@0: ret = 0; sl@0: } sl@0: else sl@0: { sl@0: t_tmp = t_now + nsec; sl@0: if (X509_cmp_time(thisupd, &t_tmp) > 0) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_NOT_YET_VALID); sl@0: ret = 0; sl@0: } sl@0: sl@0: /* If maxsec specified check thisUpdate is not more than maxsec in the past */ sl@0: if (maxsec >= 0) sl@0: { sl@0: t_tmp = t_now - maxsec; sl@0: if (X509_cmp_time(thisupd, &t_tmp) < 0) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_TOO_OLD); sl@0: ret = 0; sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: if (!nextupd) return ret; sl@0: sl@0: /* Check nextUpdate is valid and not more than nsec in the past */ sl@0: if (!ASN1_GENERALIZEDTIME_check(nextupd)) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_ERROR_IN_NEXTUPDATE_FIELD); sl@0: ret = 0; sl@0: } sl@0: else sl@0: { sl@0: t_tmp = t_now - nsec; sl@0: if (X509_cmp_time(nextupd, &t_tmp) < 0) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_STATUS_EXPIRED); sl@0: ret = 0; sl@0: } sl@0: } sl@0: sl@0: /* Also don't allow nextUpdate to precede thisUpdate */ sl@0: if (ASN1_STRING_cmp(nextupd, thisupd) < 0) sl@0: { sl@0: OCSPerr(OCSP_F_OCSP_CHECK_VALIDITY, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE); sl@0: ret = 0; sl@0: } sl@0: sl@0: return ret; sl@0: }