a ‚oewã@sdZddlmZddlmZddlZddlZddlZddlZddl m Z ddl m Z ddl m Z ddl m Z dd l mZz„Z.eeed?œd@dA„Z/eed4œdBdC„Z0eeddœdDdE„Z1d_e eeee dFœdGdH„Z2ee ee eeffdIœdJdK„Z3eedd,œdLdM„Z4eedd,œdNdO„Z5e eefedPœdQdR„Z6eeedœdSdT„Z7e e edUœdVdW„Z8e dXœdYdZ„Z9dS)`z;Compat module to handle files security on Windows and Linuxé)Úabsolute_import)ÚcontextmanagerN)ÚAny)ÚDict)Ú Generator)ÚList)ÚOptionalTFc@seZdZdZddœdd„ZdS)Ú _WindowsUmaskz+Store the current umask to apply on WindowsN©ÚreturncCs d|_dS)Né)Úmask)Úself©rú:C:\Program Files\Certbot\pkgs\certbot\compat\filesystem.pyÚ__init__$sz_WindowsUmask.__init__)Ú__name__Ú __module__Ú __qualname__Ú__doc__rrrrrr "sr )Ú file_pathÚmoder cCs trt ||¡n t||ƒdS)a[ Apply a POSIX mode on given file_path: - for Linux, the POSIX mode will be directly applied using chmod, - for Windows, the POSIX mode will be translated into a Windows DACL that make sense for Certbot context, and applied to the file using kernel calls. The definition of the Windows DACL that correspond to a POSIX mode, in the context of Certbot, is explained at https://github.com/certbot/certbot/issues/6356 and is implemented by the method `_generate_windows_flags()`. :param str file_path: Path of the file :param int mode: POSIX mode to apply N)Ú POSIX_MODEÚosÚchmodÚ_apply_win_mode©rrrrrr+sr)r r cCstrt |¡Stj}|t_|S)a$ Set the current numeric umask and return the previous umask. On Linux, the built-in umask method is used. On Windows, our Certbot-side implementation is used. :param int mask: The user file-creation mode mask to apply. :rtype: int :return: The previous umask value. )rrÚumaskÚ_WINDOWS_UMASKr )r Zprevious_umaskrrrr@s  r)NNNccs>d}z"t|ƒ}dVW|dur:t|ƒn|dur8t|ƒ0dS)z Apply a umask temporarily, meant to be used in a `with` block. Uses the Certbot implementation of umask. :param int mask: The user file-creation mode mask to apply temporarily N)r)r Z old_umaskrrrÚ temp_umaskQs ÿr)ÚsrcÚdstrÚ copy_userÚ copy_groupr cCsVtr:t |¡}|r|jnd}|r&|jnd}t |||¡n|rHt||ƒt||ƒdS)aô Copy ownership (user and optionally group on Linux) from the source to the destination, then apply given mode in compatible way for Linux and Windows. This replaces the os.chown command. :param str src: Path of the source file :param str dst: Path of the destination file :param int mode: Permission mode to apply on the destination file :param bool copy_user: Copy user if `True` :param bool copy_group: Copy group if `True` on Linux (has no effect on Windows) éÿÿÿÿN)rrÚstatÚst_uidÚst_gidÚchownÚ_copy_win_ownershipr)r r!rr"r#ÚstatsÚuser_idÚgroup_idrrrÚcopy_ownership_and_apply_modeks   r-)r r!r"r#r cCsbtrFt |¡}|r|jnd}|r&|jnd}t |||¡t||jƒn|rTt||ƒt ||ƒdS)aU Copy ownership (user and optionally group on Linux) and mode/DACL from the source to the destination. :param str src: Path of the source file :param str dst: Path of the destination file :param bool copy_user: Copy user if `True` :param bool copy_group: Copy group if `True` on Linux (has no effect on Windows) r$N) rrr%r&r'r(rÚst_moder)Ú_copy_win_mode)r r!r"r#r*r+r,rrrÚcopy_ownership_and_modeŒs   r0cCs$trt t |¡j¡|kSt||ƒS)aa Check if the given mode matches the permissions of the given file. On Linux, will make a direct comparison, on Windows, mode will be compared against the security model. :param str file_path: Path of the file :param int mode: POSIX mode to test :rtype: bool :return: True if the POSIX mode matches the file permissions )rr%ÚS_IMODErr.Ú_check_win_moderrrrÚ check_mode¥s r3)rr cCs8trt |¡jt ¡kSt |tj¡}| ¡}t ƒ|kS)zÁ Check if given file is owned by current user. :param str file_path: File path to check :rtype: bool :return: True if given file is owned by current user, False otherwise. ) rrr%r&ÚgetuidÚ win32securityÚGetFileSecurityÚOWNER_SECURITY_INFORMATIONÚGetSecurityDescriptorOwnerÚ_get_current_user)rÚsecurityÚuserrrrÚ check_owner¶s r<cCst|ƒot||ƒS)zý Check if given file has the given mode and is owned by current user. :param str file_path: File path to check :param int mode: POSIX mode to check :rtype: bool :return: True if file has correct mode and owner, False otherwise. )r<r3rrrrÚcheck_permissionsÉs r=éÿ)rÚflagsrr c CsNtrt |||¡S|tj@r4|tj@r.tjntj}t  ¡}|j }t ƒ}t ||t jƒ}| |d¡| d|d¡d}z–z$t |tjtjtj@||dd¡}Wn^tjyø} zD| jtjkrÆttj| jƒ‚| jtjkràttj| jƒ‚| ‚WYd} ~ n d} ~ 00W|r|  ¡n|r|  ¡0t ||tjAtjA¡St ||¡} t!||ƒ| S)aw Wrapper of original os.open function, that will ensure on Windows that given mode is correctly applied. :param str file_path: The file path to open :param int flags: Flags to apply on file while opened :param int mode: POSIX mode to apply on file when opened, Python defaults will be applied if ``None`` :returns: the file descriptor to the opened file :rtype: int :raise: OSError(errno.EEXIST) if the file already exists and os.O_CREAT & os.O_EXCL are set, OSError(errno.EACCES) on Windows if the file already exists and is a directory, and os.O_CREAT is set. réN)"rrÚopenÚO_CREATÚO_EXCLÚwin32conZ CREATE_NEWZ CREATE_ALWAYSr5ÚSECURITY_ATTRIBUTESÚSECURITY_DESCRIPTORr9Ú_generate_daclrr ÚSetSecurityDescriptorOwnerÚSetSecurityDescriptorDaclÚ win32fileZ CreateFileZ GENERIC_READZFILE_SHARE_READZFILE_SHARE_WRITEÚ pywintypesÚerrorÚwinerrorZERROR_FILE_EXISTSÚOSErrorÚerrnoÚEEXISTÚstrerrorZERROR_SHARING_VIOLATIONZEACCESÚCloser) rr?rZ dispositionÚ attributesr:r;ÚdaclÚhandleÚerrÚfdrrrrAÕs<    þ   ÿ   rAcCs‚tdƒ}zjt|d|ABƒtr4t ||¡Wt|ƒStj}z$tt_t ||¡W|t_Wt|ƒS|t_0Wt|ƒn t|ƒ0dS)a4 Rewrite of original os.makedirs function, that will ensure on Windows that given mode is correctly applied. :param str file_path: The file path to open :param int mode: POSIX mode to apply on leaf directory when created, Python defaults will be applied if ``None`` rr>N)rrrÚmakedirsÚmkdir)rrZ current_umaskZ orig_mkdir_fnrrrrXs"  õ þü rXc Cs¬trt ||¡St ¡}|j}tƒ}t||tj ƒ}|  |d¡|  d|d¡zt   ||¡WnJtjy¦}z0|jtjkrŽttj|j||jƒ‚|‚WYd}~n d}~00dS)a, Rewrite of original os.mkdir function, that will ensure on Windows that given mode is correctly applied. :param str file_path: The file path to open :param int mode: POSIX mode to apply on directory when created, Python defaults will be applied if ``None`` Fr@rN)rrrYr5rErFr9rGrr rHrIrJZCreateDirectoryrKrLrMZERROR_ALREADY_EXISTSrNrOrPrQ)rrrSr:r;rTrVrrrrY:s    rY)r r!r cCs,ttdƒrttdƒ||ƒn t ||¡dS)zµ Rename a file to a destination path and handles situations where the destination exists. :param str src: The current file path. :param str dst: The new file path. ÚreplaceN)ÚhasattrrÚgetattrÚrename)r r!rrrrZYs rZcCsª|}tstjdkr characters) is encountered on Windows z\\?\iéNz3Long paths are not supported by Certbot on Windows.)rrgrÚ startswithÚlenÚ ValueError)rmrbrrrrgŒs   rg)rbr cCs&trtj |¡ot |tj¡St|ƒS)z‰ Is path an executable file? :param str path: path to test :return: True if path is an executable file :rtype: bool )rrrbÚisfileÚaccessÚX_OKÚ_win_is_executable)rbrrrÚ is_executable®srvcCsVtr tt t |¡j¡tj@ƒSt |tj ¡}|  ¡}t|  tj tj t d¡dœ¡ƒS)zÒ Check if everybody/world has any right (read/write/execute) on a file given its path. :param str path: path to test :return: True if everybody/world has any right to the file :rtype: bool úS-1-1-0©Z TrusteeFormZ TrusteeTypeZ Identifier)rÚboolr%r1rr.ÚS_IRWXOr5r6ÚDACL_SECURITY_INFORMATIONÚGetSecurityDescriptorDaclÚGetEffectiveRightsFromAclÚTRUSTEE_IS_SIDÚTRUSTEE_IS_USERÚConvertStringSidToSid)rbr:rTrrrÚhas_world_permissions¼sýr)Úold_keyÚ base_moder cCs:tr6t t |¡j¡tjtjBtjBtjB@}||BS|S)a Calculate the POSIX mode to apply to a private key given the previous private key. :param str old_key: path to the previous private key :param int base_mode: the minimum modes to apply to a private key :return: the POSIX mode to apply :rtype: int ) rr%r1rr.ÚS_IRGRPÚS_IWGRPÚS_IXGRPÚS_IROTH)r‚rƒZold_moderrrÚcompute_private_key_modeÑs ÿrˆ)Úpath1Úpath2r cCsdtr0t |¡}t |¡}|j|jf|j|jfkSt |tj¡}| ¡}t |tj¡}| ¡}||kS)as Return True if the ownership of two files given their respective path is the same. On Windows, ownership is checked against owner only, since files do not have a group owner. :param str path1: path to the first file :param str path2: path to the second file :return: True if both files have the same ownership, False otherwise :rtype: bool ) rrr%r&r'r5r6r7r8)r‰rŠZstats1Zstats2Z security1Zuser1Z security2Zuser2rrrÚhas_same_ownershipæs   r‹)rbÚmin_moder c Csªtrt |¡j}|||BkSt|ƒ}t |tjtjB¡}|  ¡}|  ¡}t ||ƒ}t |  ¡ƒD]F}| |¡}|d} |d}| tjtj|dœ¡} | | | Bkr^dSq^dS)a” Check if a file given its path has at least the permissions defined by the given minimal mode. On Windows, group permissions are ignored since files do not have a group owner. :param str path: path to the file to check :param int min_mode: the minimal permissions expected :return: True if the file matches the minimal permissions expectations, False otherwise :rtype: bool r@érxFT)rrr%r.rcr5r6r7r{r8r|rGÚrangeÚ GetAceCountÚGetAcer}r~r) rbrŒr.r:r;rTZmin_daclÚindexZmin_acer Zeffective_maskrrrÚhas_min_permissionsÿs,    ÿ  ý r’cCsNtj |¡sdSt |tj¡}| ¡}| tjtj t ƒdœ¡}|t j @t j kS)NFrx) rrbrrr5r6r{r|r}r~rr9Ú ntsecurityconÚFILE_GENERIC_EXECUTE)rbr:rTrrrrru,s ýrucCsJt|ƒ}t |tj¡}| ¡}t||ƒ}| d|d¡t |tj|¡dS)zà This function converts the given POSIX mode into a Windows ACL list, and applies it to the file given its path. If the given path is a symbolic link, it will resolved to apply the mode on the targeted file. r@rN) rcr5r6r7r8rGrIÚSetFileSecurityr{)rrr:r;rTrrrr<s  r)Úuser_sidrr r c Cs¾|r|d|@}t|ƒ}t d¡}t d¡}t d¡}t ¡}|||fvrjt|dƒ}|rj| tj||¡t|dƒ} | rŠ| tj| |¡tddddœƒ} | tj| |¡| tj| |¡|S) Nr>zS-1-5-18z S-1-5-32-544rwr;ÚallT©ÚreadÚwriteÚexecute)Ú _analyze_moder5r€ZACLÚ_generate_windows_flagsZAddAccessAllowedAceZ ACL_REVISION) r–rr ZanalysisÚsystemZadminsZeveryonerTZ user_flagsZeverybody_flagsZfull_permissionsrrrrGOs$       rG)rr cCs>|tj@|tj@|tj@dœ|tj@|tj@|tj@dœdœS)Nr˜)r;r—)r%ÚS_IRUSRÚS_IWUSRÚS_IXUSRr‡ÚS_IWOTHÚS_IXOTH)rrrrrœssýýúrœcCsLt|ƒ}t |tj¡}| ¡}t |tj¡}| |d¡t |tj|¡dS©NF)rcr5r6r7r8rHr•)r r!Ú security_srcZuser_srcÚ security_dstrrrr)‚s  r)cCsNt|ƒ}t |tj¡}| ¡}t |tj¡}| d|d¡t |tj|¡dS)Nr@r)rcr5r6r{r|rIr•)r r!r¥rTr¦rrrr/‘s r/)Ú rights_descr cCsJd}|dr|tjB}|dr4|tjtjAtjAB}|drF|tjB}|S)Nrr™ršr›)r“ZFILE_GENERIC_READZFILE_ALL_ACCESSr”)r§Úflagrrrržs ÿþ rcCsHt|ƒ}t |tjtjB¡}| ¡}| ¡}|s4dSt||ƒ}t||ƒSr¤) rcr5r6r7r{r|r8rGÚ_compare_dacls)rrr:rTr;Zref_daclrrrr2½s ÿ r2)Údacl1Údacl2r cs4‡fdd„tˆ ¡ƒDƒ‡fdd„tˆ ¡ƒDƒkS)z¥ This method compare the two given DACLs to check if they are identical. Identical means here that they contains the same set of ACEs in the same order. csg|]}ˆ |¡‘qSr©r©Ú.0r‘)rªrrÚ Øóz"_compare_dacls..csg|]}ˆ |¡‘qSrr¬r­)r«rrr¯Ùr°)rŽr©rªr«rr±rr©Ósÿr©r cCs$d t ¡t ¡¡}t d|¡dS)z= Return the pySID corresponding to the current user. z{0}\{1}Nr)rfÚwin32apiZ GetDomainNameZ GetUserNamer5ZLookupAccountName)Z account_namerrrr9Üsr9)TT)r>)r>)r>)N):rZ __future__rÚ contextlibrrOrr%r`Útypingrrrrrr“rKr²rDrJr5rMÚ ImportErrorrr rÚstrÚintrrrryr-r0r3r<r=rArXrYrZrcrgrvrrˆr‹r’rurrGrœr)r/rr2r©r9rrrrÚsv          ÿ "ÿÿ  E ""-$