o
    6aM:                  
   @   s  d Z ddlmZ ddlmZ ddlZddlZddlZddlmZ ddlmZ ddlm	Z	 ddl
mZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlZddlZddlmZ ddlmZ ddlmZ ddlmZ ddlmZ zddlmZ e ej!d W n e"e#fy   dZY nw e$e%Z&G dd dZ'de(de	ee( ee( f fddZ)de(de(de(de*de+f
ddZ,d d! Z-d"d# Z.d$d% Z/dS )&z*Tools for checking certificate revocation.    )datetime)	timedeltaN)PIPE)Optional)Tuple)x509)InvalidSignature)UnsupportedAlgorithm)default_backend)hashes)serialization)crypto_util)errors)util)getenv)RenewableCert)ocspsignature_hash_algorithmc                   @   sj   e Zd ZdZdddZdedefddZdd
edede	defddZ
d
edededede	defddZdS )RevocationCheckerzEThis class figures out OCSP checking on this system, and performs it.Fc                 C   s~   d| _ |pt | _| jr=tdstd d| _ d S tjg dt	t	ddt
 d}d|jv r6dd	 | _d S d
d	 | _d S d S )NFopensslz-openssl not installed, can't check revocationT)r   r   -headervarval)stdoutstderruniversal_newlinescheckenvz	Missing =c                 S   s
   d|  gS )NzHost= hostr   r   ./usr/lib/python3/dist-packages/certbot/ocsp.py<lambda>8   s   
 z,RevocationChecker.__init__.<locals>.<lambda>c                 S   s   d| gS )NHostr   r   r   r   r!   r"   :   s    )brokenr   use_openssl_binaryr   
exe_existsloggerinfo
subprocessrunr   env_no_snap_for_external_callsr   	host_args)selfenforce_openssl_binary_usagetest_host_formatr   r   r!   __init__)   s   



zRevocationChecker.__init__certreturnc                 C   s   |  |j|jS )a   Get revoked status for a particular cert version.

        .. todo:: Make this a non-blocking call

        :param `.interfaces.RenewableCert` cert: Certificate object
        :returns: True if revoked; False if valid or the check failed or cert is expired.
        :rtype: bool

        )ocsp_revoked_by_paths	cert_path
chain_path)r-   r1   r   r   r!   ocsp_revoked<   s   
zRevocationChecker.ocsp_revoked
   r4   r5   timeoutc                 C   sj   | j rdS tjt }t||krdS t|\}}|r |s"dS | j	r.| 
|||||S t||||S )aE  Performs the OCSP revocation check

        :param str cert_path: Certificate filepath
        :param str chain_path: Certificate chain
        :param int timeout: Timeout (in seconds) for the OCSP query

        :returns: True if revoked; False if valid or the check failed or cert is expired.
        :rtype: bool

        F)r$   pytzUTCfromutcr   utcnowr   notAfter_determine_ocsp_serverr%   _check_ocsp_openssl_bin_check_ocsp_cryptography)r-   r4   r5   r8   nowurlr    r   r   r!   r3   H   s   z'RevocationChecker.ocsp_revoked_by_pathsr    rB   c                 C   s  t d}t d}d }|d us|d ur|d ur|n|}|d u r#d|g}	n|dr0|tdd  }d|d|g}	ddd	d
|d|d|d|ddt|dg| | |	 }
td| td|
 ztj	|
tjd\}}W n t
jy{   td| Y dS w t|||S )N
http_proxy
HTTP_PROXYz-urlzhttp://z-hostz-pathr   r   z	-no_noncez-issuerz-certz-CAfilez-verify_otherz-trust_otherz-timeoutr   zQuerying OCSP for %s )log*OCSP check failed for %s (are we offline?)F)r   
startswithlenstrr,   r'   debugjoinr   
run_scriptr   SubprocessErrorr(   _translate_ocsp_query)r-   r4   r5   r    rB   r8   env_http_proxyenv_HTTP_PROXY
proxy_hosturl_optscmdoutputerrr   r   r!   r?   e   sB   

	z)RevocationChecker._check_ocsp_openssl_binN)F)r7   )__name__
__module____qualname____doc__r0   r   boolr6   rJ   intr3   r?   r   r   r   r!   r   &   s    

r   r4   r2   c              	      s   t | d}t| t }W d   n1 sw   Y  z|jtj}tjj	  fdd|j
D }|d jj
}W n tjtfyN   td|  Y dS w | }|dd	 d
}|rc||fS td||  dS )zExtract the OCSP server host from a certificate.

    :param str cert_path: Path to the cert we're checking OCSP for
    :rtype tuple:
    :returns: (OCSP server URL or None, OCSP server host or None)

    rbNc                    s   g | ]	}|j  kr|qS r   )access_method).0descriptionocsp_oidr   r!   
<listcomp>   s    
z*_determine_ocsp_server.<locals>.<listcomp>r   zCannot extract OCSP URI from %s)NNz://   /z;Cannot process OCSP host from URL (%s) in certificate at %s)openr   load_pem_x509_certificatereadr
   
extensionsget_extension_for_classAuthorityInformationAccessAuthorityInformationAccessOIDOCSPvalueaccess_locationExtensionNotFound
IndexErrorr'   r(   rstrip	partition)r4   file_handlerr1   	extensiondescriptionsrB   r    r   ra   r!   r>      s$   r>   r5   rB   r8   c              
   C   s(  t |d}t| t }W d    n1 sw   Y  t | d}t| t }W d    n1 s7w   Y  t }|||t	 }|
 }|tjj}	ztj||	ddi|d}
W n tjjyu   tjd| dd Y dS w |
jd	krtd
| |
j dS t|
j}|jtjjkrtd| |j dS z	t||||  W n_ ty } ztt| W Y d }~dS d }~w tj y } ztt| W Y d }~dS d }~w t!y   td|  Y dS  t"y } ztd| t| W Y d }~dS d }~ww t#d| |j$ |j$tj%j&kS )Nr]   zContent-Typezapplication/ocsp-request)dataheadersr8   rG   T)exc_infoF   z*OCSP check failed for %s (HTTP status: %d)z'Invalid OCSP response status for %s: %sz)Invalid signature on OCSP response for %sz!Invalid OCSP response for %s: %s.z%OCSP certificate status for %s is: %s)'rf   r   rg   rh   r
   r   OCSPRequestBuilderadd_certificater   SHA1buildpublic_bytesr   EncodingDERrequestspost
exceptionsRequestExceptionr'   r(   status_codeload_der_ocsp_responsecontentresponse_statusOCSPResponseStatus
SUCCESSFULwarning_check_ocsp_responser	   rJ   r   Errorr   AssertionErrorrK   certificate_statusOCSPCertStatusREVOKED)r4   r5   rB   r8   rt   issuerr1   builderrequestrequest_binaryresponseresponse_ocspeerrorr   r   r!   r@      sd   

	r@   c                 C   s   | j |j kr
tdt| || t| jt|jr%| j|jks%| j|jkr)tdt	 }| j
s4td| j
|tdd krBtd| jrS| j|tdd k rUtddS dS )	z2Verify that the OCSP is valid for several criteriazMthe certificate in response does not correspond to the certificate in requestz<the issuer does not correspond to issuer of the certificate.zparam thisUpdate is not set.   )minutesz"param thisUpdate is in the future.z param nextUpdate is in the past.N)serial_numberr   _check_ocsp_response_signature
isinstancehash_algorithmtypeissuer_key_hashissuer_name_hashr   r<   this_updater   next_update)r   request_ocspissuer_certr4   rA   r   r   r!   r      s   r   c              	      s
  dd  j |jksj |krtd| |}nZtd|  fddjD }|s1td|d }|j|jkr?td	z|j	t
j}t
jjj|jv }W n t
jtfy^   d
}Y nw |setd|j}t| |j|j| j}t| jj| dS )zIVerify an OCSP response signature against certificate issuer or responderc                 S   s   t j|  jS )N)r   SubjectKeyIdentifierfrom_public_key
public_keydigest)r1   r   r   r!   	_key_hash   s   z1_check_ocsp_response_signature.<locals>._key_hashzGOCSP response for certificate %s is signed by the certificate's issuer.zGOCSP response for certificate %s is delegated to an external responder.c                    s*   g | ]}j |jksj |kr|qS r   )responder_namesubjectresponder_key_hash)r_   r1   r   r   r   r!   rc     s
    z2_check_ocsp_response_signature.<locals>.<listcomp>z0no matching responder certificate could be foundr   z?responder certificate is not signed by the certificate's issuerFz<responder is not authorized by issuer to sign OCSP responsesN)r   r   r   r'   rK   certificatesr   r   ri   rj   r   ExtendedKeyUsageoidExtendedKeyUsageOIDOCSP_SIGNINGrn   rp   rq   r   r   verify_signed_payloadr   	signaturetbs_certificate_bytestbs_response_bytes)r   r   r4   responder_certresponder_certsru   delegate_authorizedchosen_hashr   r   r!   r      s@   r   c           	         s   d} fdd|D }fdd|D \}}}|r| dnd}d|vs*|r(|s*|r9td	  td
| dS |r?|s?dS |rP| d}|rNtd| dS td| dS )z7Parse openssl's weird output to work out what it means.)goodrevokedunknownc                    s   g | ]}d   |qS )z{0}: (WARNING.*)?{1})format)r_   s)r4   r   r!   rc   6  s    z)_translate_ocsp_query.<locals>.<listcomp>c                 3   s"    | ]}t j| t jd V  qdS ))flagsN)researchDOTALL)r_   p)ocsp_outputr   r!   	<genexpr>7  s     z(_translate_ocsp_query.<locals>.<genexpr>   NzResponse verify OKz#Revocation status for %s is unknownzUncertain output:
%s
stderr:
%sFzOCSP revocation warning: %sTz2Unable to properly parse OCSP output: %s
stderr:%s)groupr'   r(   rK   r   )	r4   r   ocsp_errorsstatespatternsr   r   r   r   r   )r4   r   r!   rO   2  s&   
rO   )0rZ   r   r   loggingr   r)   r   typingr   r   cryptographyr   cryptography.exceptionsr   r	   cryptography.hazmat.backendsr
   cryptography.hazmat.primitivesr   r   r9   r   certbotr   r   r   certbot.compat.osr   certbot.interfacesr   cryptography.x509r   getattrOCSPResponseImportErrorAttributeError	getLoggerrW   r'   r   rJ   r>   r\   r[   r@   r   r   rO   r   r   r   r!   <module>   sF    
"e1"6