3 گau@sdZddlmZddlZddlZddlZddlZddlmZddlm Z ddlm Z ddlm Z yZ,eedd d?d@Z-dZeee eedAdBdCZ.ee ee eeffdDdEdFZ/eedd'dGdHZ0eedd'dIdJZ1e eefedKdLdMZ2eeed dNdOZ3eeedPdQdRZ4edSdTdUZ5dS)[z;Compat module to handle files security on Windows and Linux)absolute_importN)Any)Dict)List)OptionalTFc@seZdZdZddddZdS) _WindowsUmaskz+Store the current umask to apply on WindowsN)returncCs d|_dS)N)mask)selfr /usr/lib/python3.6/filesystem.py__init__"sz_WindowsUmask.__init__)__name__ __module__ __qualname____doc__rr r r r r sr) file_pathmodercCs trtj||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_MODEoschmod_apply_win_mode)rrr r r r)sr)r rcCstrtj|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. )rrumask_WINDOWS_UMASKr )r Zprevious_umaskr r r r>s  r)srcdstr copy_user copy_grouprcCsVtr:tj|}|r|jnd}|r&|jnd}tj|||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) Nr )rrstatst_uidst_gidchown_copy_win_ownershipr)rrrrrstatsuser_idgroup_idr r r copy_ownership_and_apply_modeXs   r))rrrrrcCsbtrFtj|}|r|jnd}|r&|jnd}tj|||t||jn|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) rNr r ) rrr!r"r#r$rst_moder%_copy_win_mode)rrrrr&r'r(r r r copy_ownership_and_modeys   r,cCs$trtjtj|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_mode)rrr r r check_modes r/)rrcCs8trtj|jtjkStj|tj}|j}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 win32securityGetFileSecurityOWNER_SECURITY_INFORMATIONGetSecurityDescriptorOwner_get_current_user)rsecurityuserr r r check_owners r8cCst|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. )r8r/)rrr r r check_permissionss r9)rflagsrrc Cs>trtj|||S|tj@r$|tj@r.tjntj}tj }|j }t }t ||t j}|j|d|jd|dd}zy$tj|tjtjtj@||dd}Wn\tjk r} z>| jtjkrttj| j| jtjkrttj| j| WYdd} ~ XnXWd|r |j Xtj||tjAtjAStj||}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. rrN)"rropenO_CREATO_EXCLwin32conZ CREATE_NEWZ CREATE_ALWAYSr1SECURITY_ATTRIBUTESSECURITY_DESCRIPTORr5_generate_daclrr SetSecurityDescriptorOwnerSetSecurityDescriptorDacl win32fileZ CreateFileZ GENERIC_READZFILE_SHARE_READZFILE_SHARE_WRITE pywintypeserrorwinerrorZERROR_FILE_EXISTSOSErrorerrnoEEXISTstrerrorZERROR_SHARING_VIOLATIONZEACCESZCloser) rr;rZ disposition attributesr6r7daclZhandleerrr r r r<s6         r<cCs^td}zFt|d|ABtr*tj||Stj}ztt_tj||S|t_XWdt|XdS)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`` riN)rrrmakedirsmkdir)rrZ current_umaskZ orig_mkdir_fnr r r rPs    rPcCstrtj||Stj}|j}t}t||tj }|j |d|j d|dyt j ||WnHtjk r}z*|jtjkrttj|j||j|WYdd}~XnXdS)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`` FrrN)rrrQr1r@rAr5rBrr rCrDrEZCreateDirectoryrFrGrHZERROR_ALREADY_EXISTSrIrJrKrL)rrrMr6r7rNrOr r r rQ's    rQ)rrrcCs,ttdrttd||n tj||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)hasattrrgetattrrename)rrr r r rRFs rRcCs|}tstjdkr characters) is encountered on Windows z\\?\iNz3Long paths are not supported by Certbot on Windows.)rr_r startswithlen ValueError)rerZr r r r_ys   r_)rZrcCs&trtjj|otj|tjSt|S)z Is path an executable file? :param str path: path to test :return: True if path is an executable file :rtype: bool )rrrZisfileaccessX_OK_win_is_executable)rZr r r is_executablesrncCsVtr ttjtj|jtj@Stj|tj }|j }t|j tj tj tjddS)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 zS-1-1-0) TrusteeForm TrusteeType Identifier)rboolr!r-rr*S_IRWXOr1r2DACL_SECURITY_INFORMATIONGetSecurityDescriptorDaclGetEffectiveRightsFromAclTRUSTEE_IS_SIDTRUSTEE_IS_USERConvertStringSidToSid)rZr6rNr r r has_world_permissionssrz)old_key base_modercCs:tr6tjtj|jtjtjBtjBtjB@}||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!r-rr*S_IRGRPS_IWGRPS_IXGRPS_IROTH)r{r|Zold_moder r r compute_private_key_modes r)path1path2rcCsdtr0tj|}tj|}|j|jf|j|jfkStj|tj}|j}tj|tj}|j}||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#r1r2r3r4)rrZstats1Zstats2Z security1Zuser1Z security2Zuser2r r r has_same_ownerships   r)rZmin_moderc Cstrtj|j}|||BkSt|}tj|tjtjB}|j }|j }t ||}xTt |j D]D}|j|}|d} |d}|jtjtj|d} | | | Bkr`dSq`WdS)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)rorprqFT)rrr!r*r[r1r2r3rtr4rurBrange GetAceCountGetAcervrwrx) rZrr*r6r7rNZmin_daclindexZmin_acer Zeffective_maskr r r has_min_permissionss(       rcCsNtjj|sdStj|tj}|j}|jtjtj t d}|t j @t j kS)NF)rorprq) rrZrjr1r2rtrurvrwrxr5 ntsecurityconFILE_GENERIC_EXECUTE)rZr6rNrr r r rms  rmcCsJt|}tj|tj}|j}t||}|jd|dtj|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. rrN) r[r1r2r3r4rBrDSetFileSecurityrt)rrr6r7rNr r r r)s  r)user_sidrr rc Cs|r|d|@}t|}tjd}tjd}tjd}tj}|||gkrjt|d}|rj|jtj||t|d} | r|jtj| |tdddd} |jtj| ||jtj| ||S) NizS-1-5-18z S-1-5-32-544zS-1-1-0r7allT)readwriteexecute) _analyze_moder1ryZACL_generate_windows_flagsZAddAccessAllowedAceZ ACL_REVISION) rrr ZanalysissystemZadminsZeveryonerNZ user_flagsZeverybody_flagsZfull_permissionsr r r rB<s$       rB)rrcCs>|tj@|tj@|tj@d|tj@|tj@|tj@ddS)N)rrr)r7r)r!S_IRUSRS_IWUSRS_IXUSRrS_IWOTHS_IXOTH)rr r r r`s  rcCsLt|}tj|tj}|j}tj|tj}|j|dtj|tj|dS)NF)r[r1r2r3r4rCr)rr security_srcZuser_src security_dstr r r r%os  r%cCsNt|}tj|tj}|j}tj|tj}|jd|dtj|tj|dS)Nrr)r[r1r2rtrurDr)rrrrNrr r r r+~s r+) rights_descrcCsJd}|dr|tjB}|dr4|tjtjAtjAB}|drF|tjB}|S)Nrrrr)rZFILE_GENERIC_READZFILE_ALL_ACCESSr)rflagr r r rs  rcCsHt|}tj|tjtjB}|j}|j}|s4dSt||}t||S)NF) r[r1r2r3rtrur4rB_compare_dacls)rrr6rNr7Zref_daclr r r r.s   r.)dacl1dacl2rcs4fddtjDfddtjDkS)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|]}j|qSr )r).0r)rr r sz"_compare_dacls..csg|]}j|qSr )r)rr)rr r rs)rr)rrr )rrr rsr)rcCs$djtjtj}tjd|dS)z= Return the pySID corresponding to the current user. z{0}\{1}Nr)r^win32apiZ GetDomainNameZ GetUserNamer1ZLookupAccountName)Z account_namer r r r5sr5)TT)r:)r:)r:)N)6rZ __future__rrJrr!rXZtypingrrrrrrFrr?rEr1rH ImportErrorrrrstrintrrrrr)r,r/r8r9r<rPrQrRr[r_rnrzrrrrmrrBrr%r+rr.rr5r r r r sf       ! E ""-$