o nnh@sddlmZddlZddlZddlZddlZddlZddlZddlZddl Z ddl Z ddl Z ddl m Z ddl mZddl mZdZdZzKe e deddlmZWdn1s`wYdd lmZdd lmZmZdd lmZdd lm Z dd l!m"Z#m$Z$m%Z%eZdZWn e&yYnwddl'm(Z(m)Z)ddl*m+Z,ddl-m.Z.ddl/m0Z0m1Z1m2Z2ddl3m4Z4ddl5m6Z6m7Z7e4Z8dZ9e:dZ;e:dZGddde>Z?Gddde(Z@ddZAdNd!d"ZBdOd#d$ZCdPd%d&ZDdPd'd(ZEd)d*ZFd+d,ZGd-d.ZHdOd/d0ZIGd1d2d2ZJGd3d4d4eJZKd5d6ZLdQd7d8ZMGd9d:d:eJZNGd;d<dd>eOZPd?d@ZQdAdBZRdOdCdDZSdOdEdFZTGdGdHdHZUGdIdJdJZVGdKdLdLZWdMeWiZXdS)R) annotationsN)hexlify) unhexlify)ErrorFignore)InvalidSignature)default_backend)hashespadding)HMAC) PBKDF2HMAC)Cipher algorithmsmodesT) AnsibleErrorAnsibleAssertionError) constants) binary_type)to_bytesto_text to_native)Display) makedirs_safe unfrackpaths$ANSIBLE_VAULT)AES256zDansible-vault requires the cryptography library in order to functionc@ eZdZdS)AnsibleVaultErrorN__name__ __module__ __qualname__r!r!I/usr/local/lib/python3.10/dist-packages/ansible/parsing/vault/__init__.pyrHrc@r)AnsibleVaultPasswordErrorNrr!r!r!r"r$Lr#r$c@r)AnsibleVaultFormatErrorNrr!r!r!r"r%Pr#r%c CsJztt|ddddddd}Wn ttfyYdSw|tr#dSdS)z Test if this is vault encrypted data blob :arg data: a byte or text string to test whether it is recognized as vault encrypted data :returns: True if it is recognized. Otherwise, False. asciistrict)encodingerrors nonstring)r(r)FT)rr UnicodeError TypeError startswithb_HEADER)datab_datar!r!r" is_encryptedTs r1c Cs:|}z||t||W||S||w)aTest if the contents of a file obj are a vault encrypted data blob. :arg file_obj: A file object that will be read from. :kwarg start_pos: A byte offset in the file to start reading the header from. Defaults to 0, the beginning of the file. :kwarg count: Read up to this number of bytes from the file to determine if it looks like encrypted vault data. The default is -1, read to the end of file. :returns: True if the file looks like a vault file. Otherwise, False. )tellseekr1read)file_obj start_poscountcurrent_positionr!r!r"is_encrypted_filejs   r:cCst|}|dd}|d}t|d}|}t|dkr+t|d}d|dd}||||fS)Nr;) splitlinesstripsplitrlenjoin)b_vaulttext_envelopedefault_vault_id b_tmpdata b_tmpheader b_version cipher_namevault_id b_ciphertextr!r!r"_parse_vaulttext_envelopes   rNc CsX|ptj}zt||WSty+}zd}|r|d|7}|d|7}t|d}~ww)aParse the vaulttext envelope When data is saved, it has a header prepended and is formatted into 80 character lines. This method extracts the information from the header and then removes the header and the inserted newlines. The string returned is suitable for processing by the Cipher classes. :arg b_vaulttext: byte str containing the data from a save file :kwarg default_vault_id: The vault_id name to use if the vaulttext does not provide one. :kwarg filename: The filename that the data came from. This is only used to make better error messages in case the data cannot be decrypted. This is optional. :returns: A tuple of byte str of the vaulttext suitable to pass to parse_vaultext, a byte str of the vault format version, the name of the cipher used, and the vault_id. :raises: AnsibleVaultFormatError: if the vaulttext_envelope format is invalid zVault envelope format error in %s: %sN)CDEFAULT_VAULT_IDENTITYrN Exceptionr%)rFrGfilenameexcmsgr!r!r"parse_vaulttext_envelopes    rWc s|std|p d}|r|dkrd}t|ddd}t|ddd}t|ddd}t||g}|dkr7|r7||d |}|g} | fd d td td D7} | dg7} d| } | S)a Add header and format to 80 columns :arg b_ciphertext: the encrypted and hexlified data as a byte string :arg cipher_name: unicode cipher name (for ex, u'AES256') :arg version: unicode vault version (for ex, '1.2'). Optional ('1.1' is default) :arg vault_id: unicode vault identifier. If provided, the version will be bumped to 1.2. :returns: a byte str that should be dumped into a file. It's formatted to 80 char columns and has the header prepended z-the cipher must be set before adding a headerz1.1defaultz1.2utf-8r'r)1.2r;csg|] }||dqS)Pr!).0irMr!r" z-format_vaulttext_envelope..rr\r@ )rrr.appendrErangerD) rMrKversionrLrJ b_vault_id b_cipher_name header_partsheader b_vaulttextr!r_r"format_vaulttext_envelopes(     "  rkc Cs4zt|WSttfy}ztd|d}~ww)Nz Vault format unhexlify error: %s)r BinasciiErrorr,r%)r0rUr!r!r" _unhexlifys   rmcCs4t|}|dd\}}}t|}t|}|||fS)Nrbr=)rmrC)rjb_saltb_crypted_hmacrMr!r!r"_parse_vaulttexts  rpc CsBzt|WSty ty }zd|}t|d}~ww)awParse the vaulttext :arg b_vaulttext: byte str containing the vaulttext (ciphertext, salt, crypted_hmac) :returns: A tuple of byte str of the ciphertext suitable for passing to a Cipher class's decrypt() function, a byte str of the salt, and a byte str of the crypted_hmac :raises: AnsibleVaultFormatError: if the vaulttext format is invalid z Vault vaulttext format error: %sN)rpr%rS)rjrUrVr!r!r"parse_vaulttexts  rqcCs|pd}|s t|dS)zCheck the secret against minimal requirements. Raises: AnsibleVaultPasswordError if the password does not meet requirements. Currently, only requirement is that the password is not None or an empty string. z#Invalid vault password was providedN)r$)secretrVr!r!r"verify_secret_is_not_emptysrsc@s.eZdZdZd ddZeddZddZdS) VaultSecretzKOpaque/abstract objects for a single vault secret. ie, a password or a key.NcCs ||_dSN_bytes)selfrwr!r!r"__init__ s zVaultSecret.__init__cC|jS)zThe secret as a bytestring. Sub classes that store text types will need to override to encode the text to bytes. rvrxr!r!r"bytesszVaultSecret.bytescCrzrurvr{r!r!r"loadszVaultSecret.loadru)rrr __doc__rypropertyr|r}r!r!r!r"rt s    rtcsHeZdZdgZd fdd ZeddZddZd d Zd d Z Z S)PromptVaultSecretzVault password (%s): Ncs6tt|j|d||_|dur|j|_dS||_dS)Nrv)superrryrLdefault_prompt_formatsprompt_formats)rxrwrLr __class__r!r"rys   zPromptVaultSecret.__init__cCrzrurvr{r!r!r"r|(szPromptVaultSecret.bytescCs||_dSru)ask_vault_passwordsrwr{r!r!r"r},szPromptVaultSecret.loadc Csg}|jD]3}|d|ji}z tj|dd}Wnty%td|jwt|t|ddd}| |q|D] }| |d|q;|rL|dSdS) NrLT)privatez$EOFError (ctrl-d) on prompt for (%s)r' simplerepr)r)r*r) rrLdisplaypromptEOFErrorrrsrrBrcconfirm)rxb_vault_passwords prompt_formatr vault_pass b_vault_passb_vault_passwordr!r!r"r/s    z%PromptVaultSecret.ask_vault_passwordscCs||krtddS)NzPasswords do not match)r)rxb_vault_pass_1b_vault_pass_2r!r!r"rGszPromptVaultSecret.confirmNNN) rrr rryrr|r}rr __classcell__r!r!rr"rs rcCs"tj|\}}|drdSdS)zWDetermine if a vault secret script is a client script that can be given --vault-id argsz-clientTF)ospathsplitextendswith)rT script_namedummyr!r!r"script_is_clientOs rcCstt|dd}tj|std|||r3t|r,tdt |t ||||dSt |||dSt |||dS)zI Get secret from file content or execute file and get secret from stdout F)followz(The vault password file %s was not foundz.The vault password file %s is a client script.)rTrLr(loaderrTr(r) rrrexistsr is_executablerrvvvvrClientScriptVaultSecretScriptVaultSecretFileVaultSecret)rTrLr(r this_pathr!r!r"get_file_vault_secret]s    rcsBeZdZd fdd ZeddZddZdd Zd d ZZ S) rNcs4tt|||_||_|pd|_d|_d|_dS)Nutf8)rrryrTrr(rw_text)rxrTr(rrr!r"ryys   zFileVaultSecret.__init__cCs$|jr|jS|jr|j|jSdSru)rwrencoder(r{r!r!r"r|s zFileVaultSecret.bytescCs||j|_dSru) _read_filerTrwr{r!r!r"r}szFileVaultSecret.loadc Cszt|d}|}Wdn1swYWnttfy3}ztd||fd}~ww|j||\}}|d}t|d|d|S)z Read a vault password from a file or if executable, execute the script and retrieve password from STDOUT rbNz)Could not read vault password file %s: %s z2Invalid vault password was provided from file (%s)rV) openr5rBOSErrorIOErrorrr_decrypt_if_vault_datars)rxrTfre b_vault_datarr!r!r"rs  zFileVaultSecret._read_filecCs$|jr d|jj|jfSd|jjS)Nz%s(filename='%s')%s())rTrrr{r!r!r"__repr__s zFileVaultSecret.__repr__r) rrr ryrr|r}rrrr!r!rr"rxs rc@s,eZdZddZddZddZddZd S) rcCs`|j|s td||}||\}}}|||||d}d|}t||d|S)Nz/The vault password script %s was not executablerz4Invalid vault password was provided from script (%s)r)rrr_build_command_run_check_resultsrBrs)rxrTcommandstdoutstderrprempty_password_msgr!r!r"rs    zScriptVaultSecret._read_filec Cs^z tj|tjd}Wnty#}z d}||j|f}t|d}~ww|\}}|||fS)N)rzpProblem running vault password script %s (%s). If this is not a script, remove the executable bit from the file. subprocessPopenPIPErrTr communicaterxrrr msg_formatrVrrr!r!r"rs  zScriptVaultSecret._runcCs$|jdkrtd|j|j|fdS)Nrz3Vault password script %s returned non-zero (%s): %s) returncoderrTrxrrpopenr!r!r"rs  z ScriptVaultSecret._check_resultscCs|jgSrurTr{r!r!r"rsz ScriptVaultSecret._build_commandN)rrr rrrrr!r!r!r"rs  rcsBeZdZdZd fdd ZddZddZd d Zd d ZZ S)rr=Ncs:tt|j|||d||_tdt|t|fdS)Nrz8Executing vault password client script: %s --vault-id %s)rrry _vault_idrrr)rxrTr(rrLrr!r"rys z ClientScriptVaultSecret.__init__c Csbz tj|tjtjd}Wnty%}z d}||j|f}t|d}~ww|\}}|||fS)N)rrzwProblem running vault password client script %s (%s). If this is not a script, remove the executable bit from the file.rrr!r!r"rs   zClientScriptVaultSecret._runcCsJ|j|jkrtd|j|j|f|jdkr#td|j|j|j|fdS)NzIVault password client script %s did not find a secret for vault-id=%s: %srz^Vault password client script %s returned non-zero (%s) when getting secret for vault-id=%s: %s)rVAULT_ID_UNKNOWN_RCrrTrrr!r!r"rs   z&ClientScriptVaultSecret._check_resultscCs"|jg}|jr|d|jg|S)Nz --vault-id)rTrextend)rxrr!r!r"rsz&ClientScriptVaultSecret._build_commandcCs(|jrd|jj|j|jfSd|jjS)Nz %s(filename='%s', vault_id='%s')r)rTrrrr{r!r!r"rs  z ClientScriptVaultSecret.__repr__NNNN) rrr rryrrrrrr!r!rr"rs rcs|sgSfdd|D}|S)zVFind all VaultSecret objects that are mapped to any of the target_vault_ids in secretscs g|] \}}|vr||fqSr!r!)r]rLrrtarget_vault_idsr!r"r` s z!match_secrets..r!secretsrmatchesr!rr" match_secretssrcCst||}|r |dSdS)zFind the best secret from secrets that matches target_vault_ids Since secrets should be ordered so the early secrets are 'better' than later ones, this just finds all the matches, then returns the first secretrN)rrr!r!r"match_best_secret s rcCsTtdt||durtd|g}t||}|r|Std|dd|Df)Nencrypt_vault_id=%szBmatch_encrypt_vault_id_secret requires a non None encrypt_vault_idzHDid not find a match for --encrypt-vault-id=%s in the known vault-ids %scSg|]\}}|qSr!r!)r]_v_vsr!r!r"r`*z1match_encrypt_vault_id_secret..)rrrrrr)rencrypt_vault_idencrypt_vault_id_matchersencrypt_secretr!r!r"match_encrypt_vault_id_secrets  rcCs>tdt||rt||dSdd|D}t||}|S)z@Find the best/first/only secret in secrets to use for encryptingr)rcSrr!r!)r]rrr!r!r"r`8rz(match_encrypt_secret..)rrrrr)rr_vault_id_matchers best_secretr!r!r"match_encrypt_secret-s rc@s@eZdZd ddZeddZd ddZddd Zdd d ZdS)VaultLibNcCs|pg|_d|_d|_dS)Nr[)rrKrJ)rxrr!r!r"ry@s  zVaultLib.__init__cCst|Sru)r1) vaulttextr!r!r"r1EszVaultLib.is_encryptedc Cs|dur|jrt|j\}}ntdt|dd}t|r!td|jr)|jtvr,d|_zt|j}Wnt yCtd |jw|rTt dt |t |fn t d t |||||}t||j|d } | S) aVault encrypt a piece of data. :arg plaintext: a text or byte string to encrypt. :returns: a utf-8 encoded byte str of encrypted data. The string contains a header identifying this as vault encrypted data and formatted to newline terminated lines of 80 characters. This is suitable for dumping as is to a vault file. If the string passed in is a text string, it will be encoded to UTF-8 before encryption. Nz2A vault password must be specified to encrypt datasurrogate_or_strictrZzinput is already encryptedr{0} cipher could not be foundz1Encrypting with vault_id "%s" and vault secret %sz3Encrypting without a vault_id using vault secret %srL)rrrrr1rrKCIPHER_WRITE_ALLOWLISTCIPHER_MAPPINGKeyErrorformatrvvvvvrencryptrk) rx plaintextrrrLsaltr b_plaintext this_cipherrMrjr!r!r"rIs.   zVaultLib.encryptcCs|j|||d\}}}|S)aDecrypt a piece of vault encrypted data. :arg vaulttext: a string to decrypt. Since vault encrypted data is an ascii text format this can be either a byte str or unicode string. :kwarg filename: a filename that the data came from. This is only used to make better error messages in case the data cannot be decrypted. :returns: a byte string containing the decrypted data and the vault-id that was used )rTobj)decrypt_and_get_vault_id)rxrrTrrrL vault_secretr!r!r"decryptws zVaultLib.decryptc st|ddd}|jdurd}|r|dt|7}t|t|s0d}|r,|dt|7}t|t||d \}}}|tvrDt|}ntd |d} |jsTtd g} d} d} rt d t | t|j| } | rt d t t |fn t dt tjs| fdd|jDt|j| }|D]\}}t dt |t |t |fz8t dt |t |f|||} | dur|} |} d}|rd|}t dt |t |t |fWn`Wqty}z ||_d}|r|dt |7}|dt |7}t j|ddd}~wty:}zt dt |t ||fWYd}~qd}~wwd}|rH|dt|7}t|| durbd}|r^|dt|7}t|| | | fS)aDecrypt a piece of vault encrypted data. :arg vaulttext: a string to decrypt. Since vault encrypted data is an ascii text format this can be either a byte str or unicode string. :kwarg filename: a filename that the data came from. This is only used to make better error messages in case the data cannot be decrypted. :returns: a byte string containing the decrypted data and the vault-id vault-secret that was used r'rY)r)r(Nz2A vault password must be specified to decrypt dataz in file %sz#input is not vault encrypted data. z %s is not a vault encrypted filerrz0Attempting to decrypt but no vault secrets foundz&Found a vault_id (%s) in the vaulttextzMWe have a secret associated with vault id (%s), will try to use to decrypt %sz\Found a vault_id (%s) in the vault text, but we do not have a associated secret (--vault-id)csg|] \}}|kr|qSr!r!)r]r_dummyrr!r"r`raz5VaultLib.decrypt_and_get_vault_id..z3Trying to use vault secret=(%s) id=%s to decrypt %sz Trying secret %s for vault_id=%sz of "%s"z3Decrypt%s successful with secret=%s and vault_id=%szThere was a vault format errorrOrPT) formattedzKTried to use the vault secret (%s) to decrypt (%s) but it failed. Error: %szBDecryption failed (no vault secrets were found that could decrypt)z on %szDecryption failed)rrrrr1rrWCIPHER_ALLOWLISTrrrrrrcrrQDEFAULT_VAULT_ID_MATCHrrrr%rwarning)rxrrTrrjrVrrKrrvault_id_matchers vault_id_usedvault_secret_used_matchesmatched_secretsvault_secret_idr file_slugrUrr!rr"rs             z!VaultLib.decrypt_and_get_vault_idrurNN) rrr ry staticmethodr1rrrr!r!r!r"r?s    .rc@seZdZd%ddZddZddZd&d d Zd d Zd%d dZd'ddZ d%ddZ d%ddZ ddZ ddZ d%ddZddZd(dd Zd!d"Zd#d$ZdS)) VaultEditorNcCs|pt|_dSru)rvault)rxrr!r!r"ryszVaultEditor.__init__c Cstj|}|dkrktd|}d}t|dK}t|D]=}|ddt|d|}t |}td||D]}| |q7| |d||| |krSt t |qWddS1sdwYdSdS)ar"Destroy a file, when shred (core-utils) is not available Unix `shred' destroys files "so that they can be recovered only with great difficulty with specialised hardware, if at all". It is based on the method from the paper "Secure Deletion of Data from Magnetic and Solid-State Memory", Proceedings of the Sixth USENIX Security Symposium (San Jose, California, July 22-25, 1996). We do not go to that length to re-implement shred in Python; instead, overwriting with a block of random data should suffice. See https://github.com/ansible/ansible/pull/13700 . ri r?wbr=N)rrgetsizeminrrdr4randomrandinturandomwriter3rfsync) rxtmp_pathfile_len max_chunk_lenpassesfhr chunk_lenr/r!r!r"_shred_file_customs$         "zVaultEditor._shred_file_customc Cs^tj|sdSz td|g}Wn ttfyd}Ynw|dkr(||t|dS)ajSecurely destroy a decrypted file Note standard limitations of GNU shred apply (For flash, overwriting would have no effect due to wear leveling; for other storage systems, the async kernel->filesystem->disk calls never guarantee data hits the disk; etc). Furthermore, if your tmp dirs is on tmpfs (ramdisks), it is a non-issue. Nevertheless, some form of overwriting the data (instead of just removing the fs index entry) is a good idea. If shred is not available (e.g. on windows, or no core-utils installed), fall back on a custom shredding method. Nshredr<r) rrisfilercallr ValueErrorrremove)rxrrr!r!r" _shred_file$s  zVaultEditor._shred_fileFc Cs@tjtj|\}}tj|tjd\}} || } z!z |r'|j ||ddWn t y5| | wWt |nt |wzt | Wnt yh} z| | tdd| t| fd} ~ ww|| } |st|| kr|jj| ||d} | | | || |tdt|t|t|f| | dS)N)suffixdirFrz&Unable to execute the command "%s": %s rzr?r@rArB)rxsrcdestrEr!r!r"r(Ys    zVaultEditor.shuffle_filescCs$tjd}t|}|||S)NEDITOR)rQconfigget_config_valueshlexrCrc)rxrT env_editoreditorr!r!r"r$hs   z!VaultEditor._editor_shell_commandru)NFNr)TrM)rrr ryrrr/r2r4r6r9r;r<rrHr'r%r(r$r!r!r!r"rs" % !+    ( O rc@szeZdZdZddZeddZeddZedd Z ed d Z edd dZ eddZ eddZ eddZd S) VaultAES256zw Vault implementation using AES-CTR with an HMAC-SHA256 authentication code. Keys are derived using PBKDF2 cCststtdSru)HAS_CRYPTOGRAPHYrNEED_CRYPTO_LIBRARYr{r!r!r"ryszVaultAES256.__init__cCs,ttd|||dtd}||}|S)Nr=i') algorithmlengthr iterationsbackend)r r SHA256CRYPTOGRAPHY_BACKENDderive) b_passwordrn key_length iv_lengthkdf b_derivedkeyr!r!r"_create_key_cryptographys  z$VaultAES256._create_key_cryptographyc Cspd}trtjjd}|||||}||d|d|}nttd|d|}|||d}|||fS)N r=z(Detected in initctr))rprAES block_sizer~rrq) clsryrnrzr{r}b_ivb_key1b_key2r!r!r"_gen_key_initctrs    zVaultAES256._gen_key_initctrc Cstt|t|t}|}ttjj  }| | || }|| 7}t |tt}| || } tt| ddt|fS)NrrZ)C_CipherrrrCTRrw encryptorr PKCS7rpadderupdatefinalizer r rvrr) rrrrcipherrrrMhmacb_hmacr!r!r"_encrypt_cryptographys  z!VaultAES256._encrypt_cryptographycCs"tjd}|s td}t|S)NVAULT_ENCRYPT_SALTr)rQrjrkrr r)r custom_saltr!r!r" _get_salts  zVaultAES256._get_saltNc Cs|durtd|dur|}n |stdt|}|j}|||\}}}tr4|||||\} } nttdd t || | g} t | } | S)Nz'The secret passed to encrypt() was Nonez)Empty or invalid salt passed to encrypt()z(Detected in encrypt)rb) rrrr|rrprrrqrEr) rrrrrrnryrrrrrMrjr!r!r"rs  zVaultAES256.encryptc Cst|tt}||z |t|Wnty(}ztd|d}~wwt t |t |t}|} td} | | || | } | S)NzHMAC verification failed: %s)r r rvrwrverifyrmrrrrrrr decryptorr runpadderr) rrMrorrrrrrrrrr!r!r"_decrypt_cryptographys"  z!VaultAES256._decrypt_cryptographycCs\t|tr t|tstdt|t|krdSd}t||D] \}}|||AO}q|dkS)z Comparing 2 byte arrays in constant time to avoid timing attacks. It would be nice if there were a library for this but hey. z6_is_equal can only be used to compare two byte stringsFr)rOrr,rDzip)b_ab_bresultb_xb_yr!r!r" _is_equalszVaultAES256._is_equalc CsLt|\}}}|j}|||\}}} tr |||||| } | Sttd)Nz(Detected in decrypt))rqr|rrprrrq) rrjrrrMrnroryrrrrr!r!r"rs zVaultAES256.decryptru)rrr r~ryrr~ classmethodrrrrrrrr!r!r!r"rots&        ror)rr2rurr)Y __future__rr\rQrr rlrerrIr!warningsbinasciirrrrlrprwcatch_warnings simplefilterDeprecationWarningcryptography.exceptionsrcryptography.hazmat.backendsrcryptography.hazmat.primitivesr r #cryptography.hazmat.primitives.hmacr )cryptography.hazmat.primitives.kdf.pbkdf2r &cryptography.hazmat.primitives.ciphersr rrr ImportErroransible.errorsrransiblerrQansible.module_utils.sixr+ansible.module_utils.common.text.convertersrrransible.utils.displayransible.utils.pathrrrr. frozensetrrrqrr$r%r1r:rNrWrkrmrprqrsrtrrrrrrrrrrrrrorr!r!r!r"s                 )  3 3)0 :~