a )g0@sddlmZmZmZeZddlZddlZddlZddl Z ddl Z ddl Z ddl Z ddl Z ddlZddlZddlmZddlmZddlmZdZdZze(ededdlmZWdn1s0Ydd lmZdd lmZm Z dd l!m"Z"dd l#m$Z$dd l%m&Z'm(Z(m)Z)eZdZWne*y>Yn0ddl+m,Z,m-Z-ddl.m/Z0ddl1m2Z2ddl3m4Z4m5Z5m6Z6ddl7m8Z8ddl9m:Z:m;Z;e8ZdZ?e>dZ@dZAGddde,ZBGdddeBZCGddde,ZDddZEdNd!d"ZFdOd#d$ZGdPd%d&ZHdQd'd(ZId)d*ZJd+d,ZKd-d.ZLdRd/d0ZMGd1d2d2ZNGd3d4d4eNZOd5d6ZPdSd7d8ZQGd9d:d:eNZRGd;d<dd>eSZTd?d@ZUdAdBZVdTdCdDZWdUdEdFZXGdGdHdHZYGdIdJdJZZGdKdLdLZ[dMe[iZ\dS)V)absolute_importdivisionprint_functionN)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@s eZdZdS)AnsibleVaultErrorN__name__ __module__ __qualname__r"r"B/usr/lib/python3.9/site-packages/ansible/parsing/vault/__init__.pyrJsrc@s eZdZdS)AnsibleVaultPasswordErrorNrr"r"r"r#r$Nsr$c@s eZdZdS)AnsibleVaultFormatErrorNrr"r"r"r#r%Rsr%c CsJztt|ddddddd}Wnttfy6YdS0|trFdSdS)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_encryptedVs r1c Cs>|}z$||t||W||S||0dS)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. N)tellseekr1read)Zfile_objZ start_poscountZcurrent_positionr"r"r#is_encrypted_filels   r7cCst|}|dd}|d}t|d}|}t|dkrVt|d}d|dd}||||fS)Nr;) splitlinesstripsplitrlenjoin)b_vaulttext_envelopedefault_vault_id b_tmpdataZ b_tmpheader b_version cipher_namevault_id b_ciphertextr"r"r#_parse_vaulttext_envelopes  rJc Csh|ptj}z t||WStyb}z4d}|r:|d|7}|d|7}t|WYd}~n d}~00dS)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)CZDEFAULT_VAULT_IDENTITYrJ Exceptionr%)rCrDfilenameexcmsgr"r"r#parse_vaulttext_envelopes    rRc s|s td|pd}|r$|dkr$d}t|ddd}t|ddd}t|ddd}t||g}|dkrn|rn||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.2r8csg|]}||dqS)Pr").0irIr"r# r=z-format_vaulttext_envelope..rrWr= )rrr.appendrBrangerA) rIrGversionrHrFZ b_vault_idZ b_cipher_nameZ header_partsheader b_vaulttextr"rZr#format_vaulttext_envelopes(     "  rbc CsDz t|WSttfy>}ztd|WYd}~n d}~00dS)Nz Vault format unhexlify error: %s)r BinasciiErrorr,r%)r0rPr"r"r# _unhexlifys rdcCs4t|}|dd\}}}t|}t|}|||fS)Nr\r:)rdr@)rab_saltb_crypted_hmacrIr"r"r#_parse_vaulttexts rgc CsVz t|WStyYn4tyP}zd|}t|WYd}~n d}~00dS)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)rgr%rN)rarPrQr"r"r#parse_vaulttexts   rhcCs|pd}|st|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$)secretrQr"r"r#verify_secret_is_not_emptysrjc@s.eZdZdZd ddZeddZddZdS) VaultSecretzKOpaque/abstract objects for a single vault secret. ie, a password or a key.NcCs ||_dSN_bytes)selfrnr"r"r#__init__szVaultSecret.__init__cCs|jS)zThe secret as a bytestring. Sub classes that store text types will need to override to encode the text to bytes. rmror"r"r#bytesszVaultSecret.bytescCs|jSrlrmrqr"r"r#loadszVaultSecret.load)N)rr r!__doc__rppropertyrrrsr"r"r"r#rk s   rkcsHeZdZdgZd fdd ZeddZddZd d Zd d Z Z S)PromptVaultSecretzVault password (%s): Ncs4tt|j|d||_|dur*|j|_n||_dS)Nrm)superrvrprHdefault_prompt_formatsprompt_formats)rornrHry __class__r"r#rp!s  zPromptVaultSecret.__init__cCs|jSrlrmrqr"r"r#rr*szPromptVaultSecret.bytescCs||_dSrl)ask_vault_passwordsrnrqr"r"r#rs.szPromptVaultSecret.loadc Csg}|jD]j}|d|ji}ztj|dd}Wn tyNtd|jYn0t|t|ddd}| |q |D]}| |d|qz|r|dSdS) NrHT)Zprivatez$EOFError (ctrl-d) on prompt for (%s)r'Z simplerepr)r)r*r) ryrHdisplaypromptEOFErrorrrjrr?r]confirm)roZb_vault_passwordsZ prompt_formatr~ vault_passZ b_vault_passZb_vault_passwordr"r"r#r|1s   z%PromptVaultSecret.ask_vault_passwordscCs||krtddS)NzPasswords do not match)r)roZb_vault_pass_1Zb_vault_pass_2r"r"r#rIszPromptVaultSecret.confirm)NNN) rr r!rxrprurrrsr|r __classcell__r"r"rzr#rvs  rvcCs"tj|\}}|drdSdS)zWDetermine if a vault secret script is a client script that can be given --vault-id argsz-clientTF)ospathsplitextendswith)rO script_namedummyr"r"r#script_is_clientQs rcCstt|dd}tj|s$td|||rft|rXtdt |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.)rOrHr(loaderrOr(r) rrrexistsr is_executablerr}vvvvrClientScriptVaultSecretScriptVaultSecretFileVaultSecret)rOrHr(rZ this_pathr"r"r#get_file_vault_secret_s    rcsBeZdZd fdd ZeddZddZdd Zd d ZZ S) rNcs4tt|||_||_|p d|_d|_d|_dS)Nutf8)rwrrprOrr(rn_text)rorOr(rrzr"r#rp{s  zFileVaultSecret.__init__cCs$|jr |jS|jr |j|jSdSrl)rnrencoder(rqr"r"r#rrs zFileVaultSecret.bytescCs||j|_dSrl) _read_filerOrnrqr"r"r#rsszFileVaultSecret.loadc Csz:t|d}|}Wdn1s.0YWn8ttfyr}ztd||fWYd}~n d}~00|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)rQ) openr5r?OSErrorIOErrorrrZ_decrypt_if_vault_datarj)rorOfreZ b_vault_datarr"r"r#rs .& zFileVaultSecret._read_filecCs$|jrd|jj|jfSd|jjS)Nz%s(filename='%s')%s())rOr{rrqr"r"r#__repr__szFileVaultSecret.__repr__)NNN) rr r!rprurrrsrrrr"r"rzr#rzs   rc@s,eZdZddZddZddZddZd S) rcCs`|j|std||}||\}}}|||||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_resultsr?rj)rorOcommandstdoutstderrprZempty_password_msgr"r"r#rs    zScriptVaultSecret._read_filec Csjztj|tjd}Wn>tyR}z&d}||j|f}t|WYd}~n d}~00|\}}|||fS)N)rzpProblem running vault password script %s (%s). If this is not a script, remove the executable bit from the file. subprocessPopenPIPErrOrZ communicaterorrrZ msg_formatrQrrr"r"r#rs zScriptVaultSecret._runcCs$|jdkr td|j|j|fdS)Nrz3Vault password script %s returned non-zero (%s): %s) returncoderrOrorrpopenr"r"r#rs  z ScriptVaultSecret._check_resultscCs|jgSrlrOrqr"r"r#rsz ScriptVaultSecret._build_commandN)rr r!rrrrr"r"r"r#rsrcsBeZdZdZd fdd ZddZddZd d Zd d ZZ S)rr:Ncs:tt|j|||d||_tdt|t|fdS)Nrz8Executing vault password client script: %s --vault-id %s)rwrrp _vault_idr}rr)rorOr(rrHrzr"r#rps  z ClientScriptVaultSecret.__init__c Csnztj|tjtjd}Wn>tyV}z&d}||j|f}t|WYd}~n d}~00|\}}|||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|jkr"td|j|j|f|jdkrFtd|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_RCrrOrrr"r"r#rs   z&ClientScriptVaultSecret._check_resultscCs"|jg}|jr|d|jg|S)Nz --vault-id)rOrextend)rorr"r"r#rsz&ClientScriptVaultSecret._build_commandcCs(|jrd|jj|j|jfSd|jjS)Nz %s(filename='%s', vault_id='%s')r)rOr{rrrqr"r"r#rs z ClientScriptVaultSecret.__repr__)NNNN) rr r!rrprrrrrr"r"rzr#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")rXrHritarget_vault_idsr"r#r[ r=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_secrets rcCsXtdt||dur"td|g}t||}|r:|Std|dd|DfdS)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 %scSsg|] \}}|qSr"r")rXZ_vZ_vsr"r"r#r[,r=z1match_encrypt_vault_id_secret..)r}rrrrr)rencrypt_vault_idZencrypt_vault_id_matchersZencrypt_secretr"r"r#match_encrypt_vault_id_secrets  rcCs>tdt||r"t||dSdd|D}t||}|S)z@Find the best/first/only secret in secrets to use for encryptingr)rcSsg|] \}}|qSr"r")rXrrr"r"r#r[:r=z(match_encrypt_secret..)r}rrrr)rrZ_vault_id_matchersZ best_secretr"r"r#match_encrypt_secret/s rc@s@eZdZd ddZeddZd ddZddd Zdd d ZdS)VaultLibNcCs|pg|_d|_d|_dS)NrV)rrGrF)rorr"r"r#rpBs zVaultLib.__init__cCst|Srl)r1) vaulttextr"r"r#r1GszVaultLib.is_encryptedc Cs|dur&|jrt|j\}}ntdt|dd}t|rBtd|jrR|jtvrXd|_zt|j}Wn"t ytd |jYn0|rt dt |t |fnt 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_strictrUzinput is already encryptedr{0} cipher could not be foundz1Encrypting with vault_id "%s" and vault secret %sz3Encrypting without a vault_id using vault secret %srH)rrrrr1rrGCIPHER_WRITE_WHITELISTCIPHER_MAPPINGKeyErrorformatr}vvvvvrencryptrb) ro plaintextrirHsaltr b_plaintext this_cipherrIrar"r"r#rKs,   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 )rOobj)decrypt_and_get_vault_id)rorrOrrrH vault_secretr"r"r#decryptys zVaultLib.decryptc st|ddd}|jdur tdt|sHd}|r@|dt|7}t|t||d\}}}|tvrpt|}ntd |d} |jstd g} d} d} rt d t | t|j| } | rt d t t |fnt d t tjs| fdd|jDt|j| }|D]V\}}t dt |t |t |fzvt dt |t |f|||} | dur|} |} d}|rd|}t dt |t |t |fWqWnty2}zL||_d}|r|dt |7}|dt |7}t j|ddWYd}~nXd}~0ty}z6t dt |t ||fWYd}~q,WYd}~n d}~00q,d}|r|dt|7}t|| durd}|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'rT)r)r(Nz2A vault password must be specified to decrypt dataz#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")rXr_dummyrr"r#r[r=z5VaultLib.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 errorrKrLT) 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)rrrr1rrrRCIPHER_WHITELISTrrr}rrr]rrMZDEFAULT_VAULT_ID_MATCHrrrr%rwarning)rorrOrrarQrrGrrZvault_id_matchers vault_id_usedvault_secret_usedZ_matchesZmatched_secretsZvault_secret_idrZ file_slugrPrr"rr#rs           ( z!VaultLib.decrypt_and_get_vault_id)N)NNN)NN)NN) rr r!rp staticmethodr1rrrr"r"r"r#rAs    . 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|_dSrl)rvault)rorr"r"r#rpszVaultEditor.__init__c Cstj|}|dkrtd|}d}t|d}t|D]z}|ddt|d|}t |}td||D]}| |qn| |d||| |krt t |q6Wdn1s0YdS)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)rrgetsizeminrr^r4randomZrandinturandomwriter3rfsync) rotmp_pathZfile_lenZ max_chunk_lenZpassesfh_Z chunk_lenr/r"r"r#_shred_file_customs        zVaultEditor._shred_file_customc Cs^tj|sdSztd|g}Wnttfy<d}Yn0|dkrP||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. Nshredr9r) rrisfilercallr ValueErrorrremove)rorrr"r"r# _shred_file#s   zVaultEditor._shred_fileFc CsRtjtj|\}}tj|tjd\}} || } zFz|rN|j ||ddWnt yn| | Yn0Wt |n t |0zt | WnHt y} z0| | tdd| t| fWYd} ~ n d} ~ 00|| } |s|| krD|jj| ||d} | | | || |tdt|t|t|f| | dS)N)suffixdirFrz&Unable to execute the command "%s": %s rz        && (zVaultEditor.write_datacCs\d}tj|r$t|}t|t|||durXt||jt ||j |j dSrl) rrrr rshutilmover rrrr)rosrcdestrr"r"r#rVs    zVaultEditor.shuffle_filescCs&tjdd}t|}|||S)NZEDITORvi)renvirongetshlexr@r])rorOZ env_editorZeditorr"r"r#res  z!VaultEditor._editor_shell_command)N)NFN)N)NN)N)N)N)Tr)rr r!rprrrrrrrr r rrrrrrr"r"r"r#rs  %! +    ( Mrc@sneZdZdZddZeddZeddZedd Z edd d Z ed dZ eddZ eddZ d S) VaultAES256zw Vault implementation using AES-CTR with an HMAC-SHA256 authentication code. Keys are derived using PBKDF2 cCsts ttdSrl)HAS_CRYPTOGRAPHYrNEED_CRYPTO_LIBRARYrqr"r"r#rp|szVaultAES256.__init__cCs,ttd|||dtd}||}|S)Nr:i') algorithmlengthrZ iterationsZbackend)rr SHA256CRYPTOGRAPHY_BACKENDZderive) b_passwordre key_length iv_lengthZkdf b_derivedkeyr"r"r#_create_key_cryptographys  z$VaultAES256._create_key_cryptographyc Cspd}tr>tjjd}|||||}||d|d|}n ttd|d|}|||d}|||fS)N r:z(Detected in initctr))r1rAES block_sizer;rr2) clsr7rer8r9r:b_ivb_key1b_key2r"r"r#_gen_key_initctrs   zVaultAES256._gen_key_initctrc Cstt|t|t}|}ttjj  }| | || }|| 7}t |tt}| || } tt| ddt|fS)NrrU)C_Cipherrr>rCTRr6 encryptorr PKCS7r?padderupdatefinalizer r r5rr) rrBrCrAcipherrGrIrIhmacb_hmacr"r"r#_encrypt_cryptographys  z!VaultAES256._encrypt_cryptographyNc Cs|durtd|dur$td}n|s2tdnt|}|j}|||\}}}trl|||||\} } n tt dd t || | g} t | } | S)Nz'The secret passed to encrypt() was Noner<z)Empty or invalid salt passed to encrypt()z(Detected in encrypt)r\) rrrrrrrDr1rOrr2rBr) r@rrirrer7rBrCrArNrIrar"r"r#rs   zVaultAES256.encryptc Cst|tt}||z|t|Wn0ty\}ztd|WYd}~n d}~00t t |t |t}|} td} | | || | } | S)NzHMAC verification failed: %s)r r r5r6rJZverifyrdr rrErr>rrF decryptorr rHunpadderrK) r@rIrfrBrCrArMrrLrQrRrr"r"r#_decrypt_cryptographys "z!VaultAES256._decrypt_cryptographycCs\t|trt|tstdt|t|kr0dSd}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)rrr,rAzip)Zb_aZb_bresultb_xZb_yr"r"r# _is_equalszVaultAES256._is_equalc CsNt|\}}}|j}|||\}}} tr>|||||| } n ttd| S)Nz(Detected in decrypt))rhrrrDr1rSrr2) r@rarirIrerfr7rBrCrArr"r"r#rs zVaultAES256.decrypt)N)rr r!rtrprr; classmethodrDrOrrSrWrr"r"r"r#r0qs        r0r)rr2)N)NN)NN)N)NNNN)N)N)]Z __future__rrrtypeZ __metaclass__r$rrrr/r(rrrwarningsZbinasciirrrrcr1r6catch_warnings simplefilterDeprecationWarningZcryptography.exceptionsr Zcryptography.hazmat.backendsr Zcryptography.hazmat.primitivesr r Z#cryptography.hazmat.primitives.hmacr Z)cryptography.hazmat.primitives.kdf.pbkdf2rZ&cryptography.hazmat.primitives.ciphersrrErr ImportErrorZansible.errorsrrZansiblerrMZansible.module_utils.sixrZansible.module_utils._textrrrZansible.utils.displayrZansible.utils.pathrrr}r. frozensetrrr2rr$r%r1r7rJrRrbrdrgrhrjrkrvrrrrrrrrrrrr0rr"r"r"r#s     *          )  3 3)0   7|