
    Vh`                        d dl mZmZmZ eZdZdZdZd dl	Z	d dl
Z
d dlZd dlZd dlZd dlmZ d dlmZmZmZ d dlmZmZmZmZ d d	lmZmZ d d
lmZmZ d dlmZ d dlm Z m!Z!m"Z"m#Z#m$Z$ d dl%m&Z& d dl'm(Z( dZ)dZ*	 d dlm+Z+ dZ,dZ/	 d dl0Z0d dl1Z0d dl2Z0d dl3m4Z5  e(e0jl                        Z7dZ8d Z9d Z:e;dk(  r e:        yy# e-$ r  ej\                         Z*dZ,Y Ww xY w# e-$ r  ej\                         Z/dZ8Y Lw xY w)    )absolute_importdivisionprint_functiona  
module: get_certificate
author: "John Westcott IV (@john-westcott-iv)"
short_description: Get a certificate from a host:port
description:
  - Makes a secure connection and returns information about the presented certificate.
  - The module uses the cryptography Python library.
  - Support SNI (L(Server Name Indication,https://en.wikipedia.org/wiki/Server_Name_Indication)) only with Python 2.7 and
    newer.
extends_documentation_fragment:
  - community.crypto.attributes
  - community.crypto.attributes.idempotent_not_modify_state
attributes:
  check_mode:
    support: none
    details:
      - This action does not modify state.
  diff_mode:
    support: N/A
    details:
      - This action does not modify state.
options:
  host:
    description:
      - The host to get the cert for (IP is fine).
    type: str
    required: true
  ca_cert:
    description:
      - A PEM file containing one or more root certificates; if present, the cert will be validated against these root certs.
      - Note that this only validates the certificate is signed by the chain; not that the cert is valid for the host presenting
        it.
    type: path
  port:
    description:
      - The port to connect to.
    type: int
    required: true
  server_name:
    description:
      - Server name used for SNI (L(Server Name Indication,https://en.wikipedia.org/wiki/Server_Name_Indication)) when hostname
        is an IP or is different from server name.
    type: str
    version_added: 1.4.0
  proxy_host:
    description:
      - Proxy host used when get a certificate.
    type: str
  proxy_port:
    description:
      - Proxy port used when get a certificate.
    type: int
    default: 8080
  starttls:
    description:
      - Requests a secure connection for protocols which require clients to initiate encryption.
      - Only available for V(mysql) currently.
    type: str
    choices:
      - mysql
    version_added: 1.9.0
  timeout:
    description:
      - The timeout in seconds.
    type: int
    default: 10
  select_crypto_backend:
    description:
      - Determines which crypto backend to use.
      - The default choice is V(auto), which tries to use C(cryptography) if available.
      - If set to V(cryptography), will try to use the L(cryptography,https://cryptography.io/) library.
    type: str
    default: auto
    choices: [auto, cryptography]
  ciphers:
    description:
      - SSL/TLS Ciphers to use for the request.
      - When a list is provided, all ciphers are joined in order with V(:).
      - See the L(OpenSSL Cipher List Format,https://www.openssl.org/docs/manmaster/man1/openssl-ciphers.html#CIPHER-LIST-FORMAT)
        for more details.
      - The available ciphers is dependent on the Python and OpenSSL/LibreSSL versions.
    type: list
    elements: str
    version_added: 2.11.0
  asn1_base64:
    description:
      - Whether to encode the ASN.1 values in the RV(extensions) return value with Base64 or not.
      - The documentation claimed for a long time that the values are Base64 encoded, but they never were. For compatibility
        this option is set to V(false).
      - The default value V(false) is B(deprecated) and will change to V(true) in community.crypto 3.0.0.
    type: bool
    version_added: 2.12.0
  tls_ctx_options:
    description:
      - TLS context options (TLS/SSL OP flags) to use for the request.
      - See the L(List of SSL OP Flags,https://wiki.openssl.org/index.php/List_of_SSL_OP_Flags) for more details.
      - The available TLS context options is dependent on the Python and OpenSSL/LibreSSL versions.
    type: list
    elements: raw
    version_added: 2.21.0
  get_certificate_chain:
    description:
      - If set to V(true), will obtain the certificate chain next to the certificate itself.
      - The chain as returned by the server can be found in RV(unverified_chain), and the chain that passed validation in
        RV(verified_chain).
      - B(Note) that this needs B(Python 3.10 or newer). Also note that only Python 3.13 or newer officially supports this.
        The module uses internal APIs of Python 3.10, 3.11, and 3.12 to achieve the same. It can be that future versions of
        Python 3.10, 3.11, or 3.12 break this.
    type: bool
    default: false
    version_added: 2.21.0

notes:
  - When using ca_cert on OS X it has been reported that in some conditions the validate will always succeed.
requirements:
  - "Python >= 2.7 when using O(proxy_host), and Python >= 3.10 when O(get_certificate_chain=true)"
  - "cryptography >= 1.6"

seealso:
  - plugin: community.crypto.to_serial
    plugin_type: filter
a0  
cert:
  description: The certificate retrieved from the port.
  returned: success
  type: str
expired:
  description: Boolean indicating if the cert is expired.
  returned: success
  type: bool
extensions:
  description: Extensions applied to the cert.
  returned: success
  type: list
  elements: dict
  contains:
    critical:
      returned: success
      type: bool
      description: Whether the extension is critical.
    asn1_data:
      returned: success
      type: str
      description:
        - The ASN.1 content of the extension.
        - If O(asn1_base64=true) this will be Base64 encoded, otherwise the raw binary value will be returned.
        - Please note that the raw binary value might not survive JSON serialization to the Ansible controller, and also might
          cause failures when displaying it. See U(https://github.com/ansible/ansible/issues/80258) for more information.
        - B(Note) that depending on the C(cryptography) version used, it is not possible to extract the ASN.1 content of the
          extension, but only to provide the re-encoded content of the extension in case it was parsed by C(cryptography).
          This should usually result in exactly the same value, except if the original extension value was malformed.
    name:
      returned: success
      type: str
      description: The extension's name.
issuer:
  description: Information about the issuer of the cert.
  returned: success
  type: dict
not_after:
  description: Expiration date of the cert.
  returned: success
  type: str
not_before:
  description: Issue date of the cert.
  returned: success
  type: str
serial_number:
  description:
    - The serial number of the cert.
    - This return value is an B(integer). If you need the serial numbers as a colon-separated hex string, such as C(11:22:33),
      you need to convert it to that form with P(community.crypto.to_serial#filter).
  returned: success
  type: int
signature_algorithm:
  description: The algorithm used to sign the cert.
  returned: success
  type: str
subject:
  description: Information about the subject of the cert (C(OU), C(CN), and so on).
  returned: success
  type: dict
version:
  description: The version number of the certificate.
  returned: success
  type: str
verified_chain:
  description:
    - The verified certificate chain retrieved from the port.
    - The first entry is always RV(cert).
    - The last certificate the root certificate the chain is traced to. If O(ca_cert) is provided this certificate is part
      of that store; otherwise it is part of the store used by default by Python.
    - Note that RV(unverified_chain) generally does not contain the root certificate, and might contain other certificates
      that are not part of the validated chain.
  returned: success and O(get_certificate_chain=true)
  type: list
  elements: str
  version_added: 2.21.0
unverified_chain:
  description:
    - The certificate chain retrieved from the port.
    - The first entry is always RV(cert).
  returned: success and O(get_certificate_chain=true)
  type: list
  elements: str
  version_added: 2.21.0
am  
---
- name: Get the cert from an RDP port
  community.crypto.get_certificate:
    host: "1.2.3.4"
    port: 3389
  delegate_to: localhost
  run_once: true
  register: cert

- name: Get a cert from an https port
  community.crypto.get_certificate:
    host: "www.google.com"
    port: 443
  delegate_to: localhost
  run_once: true
  register: cert

- name: How many days until cert expires
  ansible.builtin.debug:
    msg: "cert expires in: {{ expire_days }} days."
  vars:
    expire_days: >-
      {{ (
        (cert.not_after | ansible.builtin.to_datetime('%Y%m%d%H%M%SZ')) -
        (ansible_date_time.iso8601 | ansible.builtin.to_datetime('%Y-%m-%dT%H:%M:%SZ'))
      ).days }}

- name: Allow legacy insecure renegotiation to get a cert from a legacy device
  community.crypto.get_certificate:
    host: "legacy-device.domain.com"
    port: 443
    ciphers:
      - HIGH
    tls_ctx_options:
      - OP_ALL
      - OP_NO_SSLv3
      - OP_CIPHER_SERVER_PREFERENCE
      - OP_ENABLE_MIDDLEBOX_COMPAT
      - OP_NO_COMPRESSION
      - 4 # OP_LEGACY_SERVER_CONNECT
  delegate_to: localhost
  run_once: true
  register: legacy_cert
N)isfile)create_connectionsetdefaulttimeoutsocket)	CERT_NONECERT_REQUIREDDER_cert_to_PEM_certget_server_certificate)AnsibleModulemissing_required_lib)to_bytes	to_native)string_types)CRYPTOGRAPHY_TIMEZONE%cryptography_get_extensions_from_certcryptography_oid_to_nameget_not_valid_afterget_not_valid_before)get_now_datetime)LooseVersionz1.6)create_default_contextTF)default_backendc                 X    |dk(  r%d}| j                  d       | j                  |       y y )Nmysqls$          !                           )recvsend)sockserver_typessl_request_packets      t/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/crypto/plugins/modules/get_certificate.pysend_starttls_packetr%   C  s9    g" 	 				
 			$%     c                     t        t        t        d      t        dd      t        dd      t        d      t        dd      t        d      t        dd	      t        dd
dgd
      t        ddg      t        dd      t        d      t        dd      t        dd                  } | j                  j                  d      }| j                  j                  d      }| j                  j                  d      }| j                  j                  d      }| j                  j                  d      }| j                  j                  d      }| j                  j                  d      }| j                  j                  d      }| j                  j                  d      }	| j                  d   }
| j                  d    }| j                  d!   }|
| j	                  d"d#d$%       d}
|r6t
        j                  d&k  r#| j                  d't
        j                  z  (       | j                  j                  d)      }|d
k(  rLt        xr t        t        t              k\  }|rd}|d
k(  r%| j                  d*j                  t              (       |dk(  r9t        s3| j                  t        d+j                  t                    t        ,       t        d-      }|rt!        |       |rt#        |      s| j                  d.(       d }d }t$        s]|r| j                  d/t&        ,       |	| j                  d0t&        ,       || j                  d1t&        ,       	 t)        ||f|2      }n	 |rvd4|d5|d6}t-               }t/        j0                  |j2                         |j5                  ||f       |j7                  |j9                                |j;                  d7       n,t=        ||f      }t/        j0                  |j2                         |rt?        |8      }d|_         tB        |_"        nt?               }d|_         tF        |_"        |tI        ||       |	"d5jK                  |	      }|jM                  |       |d9|_'        |D ]  }tQ        |tR              rQtU        |      }tW        tX        |d       }tQ        |tZ              r|}nX| j                  d:j                  |      (       n6tQ        |tZ              r|}n#| j                  d;j                  |      (       d9}	 |xjN                  z  c_'         |j]                  ||xs |=      }|j_                  d      }ta        |      }|rt
        j                  d>k  r<d? }|jb                  } ||je                               } ||jg                               } n/d@ } ||je                               } ||jg                               } |D !cg c]  }!ta        |!       }}!| D !cg c]  }!ta        |!       }}!|dB<   |dk(  rCth        jj                  jm                  to        |      tq                     }"i |dC<   |"jr                  D ])  }#|#jt                  |dC   tw        |#jx                  dD      <   + t{        |"      t}        t~        E      k  |dF<   g |dG<   t        |"      j                         D ]v  \  }$}%th        jj                  jx                  j                  |$      }&|%dH   |%dI   tw        |&dD      dJ}'|
st        j                  |'dK         |'dK<   |dG   j                  |'       x i |dL<   |"j                  D ])  }#|#jt                  |dL   tw        |#jx                  dD      <   + t{        |"      j                  dM      |dN<   t        |"      j                  dM      |dO<   |"j                  |dP<   tw        |"j                        |dQ<   |"j                  th        jj                  j                  j                  k(  rd9|dR<   n<|"j                  th        jj                  j                  j                  k(  rdS|dR<   ndT|dR<   |||dU<   |||dV<    | j                  dWi | y # t*        $ r.}| j                  d3j                  |||      (       Y d }~d }~ww xY w# t*        $ r) | j                  d<j                  xs       (       Y aw xY wc c}!w c c}!w # t*        $ rV}|r&| j                  dAj                  |||||      (       n#| j                  d3j                  |||      (       Y d }~4d }~ww xY w)XNpath)typestrT)r)   requiredinti  )r)   default
   autocryptography)r)   choicesr-   r   )r)   r1   list)r)   elementsboolrawF)ca_certhostport
proxy_host
proxy_portserver_nametimeoutselect_crypto_backendstarttlsciphersasn1_base64tls_ctx_optionsget_certificate_chain)argument_specr6   r7   r8   r9   r:   r<   r;   r>   r?   r@   rA   rB   zThe default value `false` for asn1_base64 is deprecated and will change to `true` in community.crypto 3.0.0. If you need this value, it is best to set the value explicitly and adjust your roles/playbooks to use `asn1_base64=true` as soon as possiblez3.0.0zcommunity.crypto)versioncollection_name)   r.   zget_certificate_chain=true can only be used with Python 3.10 (Python 3.13+ officially supports this). The Python version used to run the get_certificate module is %s)msgr=   z?Cannot detect the required Python library cryptography (>= {0})zcryptography >= {0})rG   	exception)changedzca_cert file does not existzTTo use proxy_host, you must run the get_certificate module with Python 2.7 or newer.zQTo use ciphers, you must run the get_certificate module with Python 2.7 or newer.zYTo use tls_ctx_options, you must run the get_certificate module with Python 2.7 or newer.)ca_certsz+Failed to get cert from {0}:{1}, error: {2}zCONNECT :z HTTP/1.0

r   )cafiler   z-Failed to determine the numeric value for {0}z6tls_ctx_options must be a string or integer, got {0!r}z Failed to add {0} to CTX options)server_hostname)rF      c                     | sg S | D cg c]+  }|j                  t        j                  j                        - c}S c c}w N)public_bytesssl_sslENCODING_DERchaincs     r$   _convert_chainzmain.<locals>._convert_chain!  s1    $#%IOTU!sxx/D/D EUUUs   0<c                     | D cg c]=  }t        |t              r|n(|j                  t        j                  j
                        ? c}S c c}w rP   )
isinstancebytesrQ   rR   rS   rT   rU   s     r$   rX   zmain.<locals>._convert_chain2  sN     &+  !" $.a#7 !"%&^^CHH4I4I%J!K    s   AA
z=Failed to get cert via proxy {0}:{1} from {2}:{3}, error: {4}certsubject)short)with_timezoneexpired
extensionscriticalvalue)rb   	asn1_datanamerd   issuerz%Y%m%d%H%M%SZ	not_after
not_beforeserial_numbersignature_algorithmrD      unknownverified_chainunverified_chain )Or   dictparamsget	deprecatesysversion_info	fail_jsonrD   CRYPTOGRAPHY_FOUNDCRYPTOGRAPHY_VERSIONr   MINIMAL_CRYPTOGRAPHY_VERSIONformatr   CRYPTOGRAPHY_IMP_ERRr   r   HAS_CREATE_DEFAULT_CONTEXTCREATE_DEFAULT_CONTEXT_IMP_ERRr   	Exceptionr	   atexitregistercloseconnectr    encoder   r   r   check_hostnamer   verify_moder
   r%   joinset_ciphersoptionsrZ   r   r   getattrrR   r,   wrap_socketgetpeercertr   _sslobjget_verified_chainget_unverified_chainr0   x509load_pem_x509_certificater   cryptography_backendr]   rc   r   oidr   r   r   r   itemsObjectIdentifierbase64	b64decodeappendrf   strftimer   ri   signature_algorithm_oidVersionv1v3	exit_json)(moduler6   r7   r8   r9   r:   r<   r;   start_tls_server_typer?   r@   rA   rB   backendcan_use_cryptographyresultrm   rn   r\   er   r!   ctxciphers_joinedtls_ctx_optiontls_ctx_option_strtls_ctx_option_attrtls_ctx_option_inttls_sockrX   ssl_objverified_der_chainunverified_der_chainrW   r   	attributedotted_numberentryr   exts(                                           r$   mainr   S  s>	   f%540540'5%(eR0"&V^$<f# uwi8fu5&) fu="&FE"B
F( mm	*G==V$D==V$D""<0J""<0Jmm	*G--##M2K"MM--j9mm	*G--.Kmm$56O"MM*AB\ . 	 	
 !1!1G!;Nkk 	 	
 mm 78G&  S$5Q(RR 	  $G fX&56   . !()001MN /	   F '"g!>?N%j8   g8   &o8  	)4,IDG	>BDIx

+j*56		'..*+		$($6

+,G<%*""/,.%*""+$0$T+@A"!$'!2/* '6 )N!.,?-6~-F*.5c;Mt.T+%&93?1D. #,,$S$Z$Z$6%" -  $NC8-;* (( X _ _ .! )   +'99G)V t[=PDQH''-D'-D$##g-
V
 '..G)78R8R8T)U&+9446,( *88S8S8U)V&+9 557,( DV!Va"6q"9!V!V5I$01(+$  $$ F6N.   ::TN02
 y 	I 96y}}DQR	
 058H/9
 
y  "|$I$$O$U$U$W 		- M5##''88GC!*-"7^0DAC
 #)#3#3C4D#EK < '',		- x 	I 85imm4PQ	
 2$7@@Q{3D9BB?S|"&"4"4(@(()
$%
 <<<,,44777 %F9\\\..66999 %F9 )F9%'5F#$')9F%&FvM  	AHHtUVW   	V % (( B I I 2 H6H! ) b "W$  	  W^^"JdA !    ELLdA ! 	so   b  ,F8c9 %b::B9c9 3c/c9 c4c9  	b7	#b22b7:.c,(c9 +c,,c9 9	eAee__main__)<
__future__r   r   r   r)   __metaclass__DOCUMENTATIONRETURNEXAMPLESr   r   rR   rt   	tracebackos.pathr   r	   r   r   r
   r   r   r   ansible.module_utils.basicr   r   +ansible.module_utils.common.text.convertersr   r   ansible.module_utils.sixr   Uansible_collections.community.crypto.plugins.module_utils.crypto.cryptography_supportr   r   r   r   r   >ansible_collections.community.crypto.plugins.module_utils.timer   Aansible_collections.community.crypto.plugins.module_utils.versionr   ry   r}   r   r|   ImportError
format_excr{   r0   cryptography.exceptionscryptography.x509cryptography.hazmat.backendsr   r   __version__rx   rw   r%   r   __name__ro   r&   r$   <module>r      s*   A @ yvU
n,\   
 
   ? ? V V J K 1 
  % !% &*
 "& "T'(@(@A
 & xv	 zF A  '%9Y%9%9%;"!&'  /9//1s$   8B= $C =CCC65C6