a
    á `˜u  ã                   @   s–  d Z ddlmZ ddlZddlZddlZddlZddlmZ z<ddl	Z	ddl
Z
ddlZddlZddlZddlZddlZW n eyŽ   dZY n0 dZG dd„ dƒZeƒ Zd	d
„ Zdd„ Zdd„ ZdBdd„Zdd„ Zdd„ Zdd„ ZdCdd„ZdDdd„ZdEdd„Zdd„ Zd d!„ Zd"d#„ Z d$d%„ Z!d&d'„ Z"d(d)„ Z#d*d+„ Z$d,d-„ Z%d.d/„ Z&d0d1„ Z'dFd2d3„Z(d4d5„ Z)d6d7„ Z*d8d9„ Z+d:d;„ Z,d<d=„ Z-d>d?„ Z.d@dA„ Z/dS )Gz;Compat module to handle files security on Windows and Linuxé    )Úabsolute_importN)ÚListTFc                   @   s   e Zd ZdZdd„ ZdS )Ú_WindowsUmaskz+Store the current umask to apply on Windowsc                 C   s
   d| _ d S )Né   )Úmask)Úself© r   ú;/usr/lib/python3/dist-packages/certbot/compat/filesystem.pyÚ__init__    s    z_WindowsUmask.__init__N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r
   r   r   r   r	   r      s   r   c                 C   s    t rt | |¡ n
t| |ƒ dS )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©Ú	file_pathÚmoder   r   r	   r   '   s    r   c                 C   s   t rt | ¡S tj}| 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.
    )r   r   ÚumaskÚ_WINDOWS_UMASKr   )r   Zprevious_umaskr   r   r	   r   =   s
    

r   c                 C   sV   t r:t | ¡}|r|jnd}|r&|jnd}t |||¡ n|rHt| |ƒ t||ƒ dS )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)r   r   ÚstatÚst_uidÚst_gidÚchownÚ_copy_win_ownershipr   )ÚsrcÚdstr   Ú	copy_userÚ
copy_groupÚstatsÚuser_idÚgroup_idr   r   r	   Úcopy_ownership_and_apply_modeX   s    

r%   c                 C   sb   t rFt | ¡}|r|jnd}|r&|jnd}t |||¡ t||jƒ n|rTt| |ƒ t	| |ƒ dS )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)
r   r   r   r   r   r   r   Úst_moder   Ú_copy_win_mode)r   r   r    r!   r"   r#   r$   r   r   r	   Úcopy_ownership_and_modey   s    

r(   c                 C   s$   t rt t | ¡j¡|kS t| |ƒ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
    )r   r   ÚS_IMODEr   r&   Ú_check_win_moder   r   r   r	   Ú
check_mode’   s    r+   c                 C   s8   t rt | ¡jt ¡ kS t | tj¡}| ¡ }t	ƒ |kS )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.
    )
r   r   r   r   ÚgetuidÚwin32securityÚGetFileSecurityÚOWNER_SECURITY_INFORMATIONÚGetSecurityDescriptorOwnerÚ_get_current_user)r   ÚsecurityÚuserr   r   r	   Úcheck_owner¤   s
    	r4   c                 C   s   t | ƒot| |ƒ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.
    )r4   r+   r   r   r   r	   Úcheck_permissions»   s    
r5   éÿ  c           
   
   C   sN  t rt | ||¡S |tj@ r4|tj@ r.tjntj}t 	¡ }|j
}tƒ }t||tjƒ}| |d¡ | d|d¡ d}z–z$t | tjtjtj@ ||dd¡}W n^ tjyø }	 zD|	jtjkrÆttj|	jƒ‚|	jtjkràttj|	jƒ‚|	‚W Y d}	~	n
d}	~	0 0 W |r|  ¡  n|r|  ¡  0 t | |tjA tjA ¡S t | |¡}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)"r   r   ÚopenÚO_CREATÚO_EXCLÚwin32conZ
CREATE_NEWZCREATE_ALWAYSr-   ÚSECURITY_ATTRIBUTESÚSECURITY_DESCRIPTORr1   Ú_generate_daclr   r   ÚSetSecurityDescriptorOwnerÚSetSecurityDescriptorDaclÚ	win32fileZ
CreateFileZGENERIC_READZFILE_SHARE_READZFILE_SHARE_WRITEÚ
pywintypesÚerrorÚwinerrorZERROR_FILE_EXISTSÚOSErrorÚerrnoÚEEXISTÚstrerrorZERROR_SHARING_VIOLATIONZEACCESZCloser   )
r   Úflagsr   ZdispositionÚ
attributesr2   r3   ÚdaclZhandleÚerrr   r   r	   r8   È   s<    

þ
ÿ

r8   c                 C   s‚   t dƒ}zjt |d|A B ƒ tr4t | |¡W t |ƒ S tj}z$tt_t | |¡W |t_W t |ƒ S |t_0 W t |ƒ n
t |ƒ 0 dS )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``
    r   r6   N)r   r   r   ÚmakedirsÚmkdir)r   r   Zcurrent_umaskZorig_mkdir_fnr   r   r	   rM     s"    
õþü
rM   c              
   C   s¬   t rt | |¡S t ¡ }|j}tƒ }t||tj	ƒ}| 
|d¡ | d|d¡ zt | |¡ W nJ tjy¦ } z0|jtjkrŽttj|j| |jƒ‚|‚W Y d}~n
d}~0 0 dS )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``
    Fr7   r   N)r   r   rN   r-   r<   r=   r1   r>   r   r   r?   r@   rA   ZCreateDirectoryrB   rC   rD   ZERROR_ALREADY_EXISTSrE   rF   rG   rH   )r   r   rJ   r2   r3   rK   rL   r   r   r	   rN   /  s    
rN   c                 C   s,   t tdƒrttdƒ| |ƒ nt | |¡ dS )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   r   r   r	   rO   O  s    
rO   c                 C   sª   | }t stjdkr<tj | ¡}tj |¡r8td |¡ƒ‚|S g }tj | ¡rž| }t 	| ¡} tj 
| ¡s|tj tj |¡| ¡} | |v r’td |¡ƒ‚| | ¡ q@tj | ¡S )a   
    Find the real path for the given path. This method resolves symlinks, including
    recursive symlinks, and is protected against symlinks that creates an infinite loop.

    :param str file_path: The path to resolve
    :returns: The real path for the given path
    :rtype: str
    )é   é   zError, link {0} is a loop!)r   ÚsysÚversion_infor   ÚpathÚrealpathÚislinkÚRuntimeErrorÚformatÚreadlinkÚisabsÚjoinÚdirnameÚappendÚabspath)r   Zoriginal_pathrW   Zinspected_pathsÚ	link_pathr   r   r	   rX   a  s     

rX   c                 C   s@   t  | ¡}ts| d¡s|S t|ƒdk r4|dd… S tdƒ‚dS )a  
    Return a string representing the path to which the symbolic link points.

    :param str link_path: The symlink path to resolve
    :return: The path the symlink points to
    :returns: str
    :raise: ValueError if a long path (260> characters) is encountered on Windows
    z\\?\i  é   Nz3Long paths are not supported by Certbot on Windows.)r   r\   r   Ú
startswithÚlenÚ
ValueError)rb   rW   r   r   r	   r\   „  s    

	r\   c                 C   s&   t rtj | ¡ot | tj¡S t| ƒS )z‰
    Is path an executable file?

    :param str path: path to test
    :return: True if path is an executable file
    :rtype: bool
    )r   r   rW   ÚisfileÚaccessÚX_OKÚ_win_is_executable)rW   r   r   r	   Úis_executable§  s    	rk   c                 C   sV   t r tt t | ¡j¡tj@ ƒS t | tj	¡}| 
¡ }t| tjtj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   r)   r   r&   ÚS_IRWXOr-   r.   ÚDACL_SECURITY_INFORMATIONÚGetSecurityDescriptorDaclÚGetEffectiveRightsFromAclÚTRUSTEE_IS_SIDÚTRUSTEE_IS_USERÚConvertStringSidToSid)rW   r2   rK   r   r   r	   Úhas_world_permissions¶  s    	ýrv   c                 C   s:   t r6t t | ¡j¡tjtjB tjB tjB @ }||B S |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
    )	r   r   r)   r   r&   ÚS_IRGRPÚS_IWGRPÚS_IXGRPÚS_IROTH)Zold_keyZ	base_modeZold_moder   r   r	   Úcompute_private_key_modeÌ  s    
ÿr{   c                 C   sd   t r0t | ¡}t |¡}|j|jf|j|jfkS t | tj¡}| ¡ }t |tj¡}| ¡ }||kS )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

    )	r   r   r   r   r   r-   r.   r/   r0   )Zpath1Zpath2Zstats1Zstats2Z	security1Zuser1Z	security2Zuser2r   r   r	   Úhas_same_ownershipâ  s    

r|   c                 C   sª   t rt | ¡j}|||B kS t| ƒ} t | tjtjB ¡}| 	¡ }| 
¡ }t||ƒ}t| ¡ ƒD ]F}| |¡}|d }	|d }| tjtj|dœ¡}
|
|
|	B kr^ dS q^dS )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
    r7   é   rm   FT)r   r   r   r&   rX   r-   r.   r/   rp   r0   rq   r>   ÚrangeÚGetAceCountÚGetAcerr   rs   rt   )rW   Zmin_moder&   r2   r3   rK   Zmin_daclÚindexZmin_acer   Zeffective_maskr   r   r	   Úhas_min_permissionsü  s,    ÿ

ýr‚   c                 C   sN   t j | ¡sdS t | tj¡}| ¡ }| tjtj	t
ƒ dœ¡}|tj@ tjkS )NFrm   )r   rW   rg   r-   r.   rp   rq   rr   rs   rt   r1   ÚntsecurityconÚFILE_GENERIC_EXECUTE)rW   r2   rK   r   r   r   r	   rj   *  s    ýrj   c                 C   sJ   t | ƒ} t | tj¡}| ¡ }t||ƒ}| d|d¡ t | tj|¡ dS )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.
    r7   r   N)	rX   r-   r.   r/   r0   r>   r@   ÚSetFileSecurityrp   )r   r   r2   r3   rK   r   r   r	   r   :  s    
r   c                 C   s¾   |r|d| @ }t |ƒ}t d¡}t d¡}t d¡}t ¡ }| ||fvrjt|d ƒ}|rj| tj|| ¡ t|d ƒ}	|	rŠ| tj|	|¡ tddddœƒ}
| tj|
|¡ | tj|
|¡ |S )	Nr6   zS-1-5-18zS-1-5-32-544rl   r3   ÚallT©ÚreadÚwriteÚexecute)Ú_analyze_moder-   ru   ZACLÚ_generate_windows_flagsZAddAccessAllowedAceZACL_REVISION)Zuser_sidr   r   ZanalysisÚsystemZadminsZeveryonerK   Z
user_flagsZeverybody_flagsZfull_permissionsr   r   r	   r>   M  s$    


r>   c                 C   s>   | t j@ | t j@ | t j@ dœ| t j@ | t j@ | t j@ dœdœS )Nr‡   )r3   r†   )r   ÚS_IRUSRÚS_IWUSRÚS_IXUSRrz   ÚS_IWOTHÚS_IXOTH)r   r   r   r	   r‹   q  s    ýýúr‹   c                 C   sL   t | ƒ} t | tj¡}| ¡ }t |tj¡}| |d¡ t |tj|¡ d S ©NF)rX   r-   r.   r/   r0   r?   r…   )r   r   Úsecurity_srcZuser_srcÚsecurity_dstr   r   r	   r   €  s    r   c                 C   sN   t | ƒ} t | tj¡}| ¡ }t |tj¡}| d|d¡ t |tj|¡ d S )Nr7   r   )rX   r-   r.   rp   rq   r@   r…   )r   r   r”   rK   r•   r   r   r	   r'     s    r'   c                 C   sJ   d}| d r|t jB }| d r4|t jt jA t jA B }| d rF|t jB }|S )Nr   rˆ   r‰   rŠ   )rƒ   ZFILE_GENERIC_READZFILE_ALL_ACCESSr„   )Zrights_descÚflagr   r   r	   rŒ   œ  s    
ÿþ
rŒ   c                 C   sH   t | ƒ} t | tjtjB ¡}| ¡ }| ¡ }|s4dS t||ƒ}t||ƒS r“   )	rX   r-   r.   r/   rp   rq   r0   r>   Ú_compare_dacls)r   r   r2   rK   r3   Zref_daclr   r   r	   r*   »  s    
ÿ
r*   c                    s4   ‡ fdd„t ˆ  ¡ ƒD ƒ‡fdd„t ˆ ¡ ƒD ƒkS )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.
    c                    s   g | ]}ˆ   |¡‘qS r   ©r€   ©Ú.0r   )Údacl1r   r	   Ú
<listcomp>Ö  ó    z"_compare_dacls.<locals>.<listcomp>c                    s   g | ]}ˆ   |¡‘qS r   r˜   r™   )Údacl2r   r	   rœ   ×  r   )r~   r   ©r›   rž   r   rŸ   r	   r—   Ñ  s    ÿr—   c                  C   s$   d  t ¡ t ¡ ¡} t d| ¡d S )z=
    Return the pySID corresponding to the current user.
    z{0}\{1}Nr   )r[   Úwin32apiZGetDomainNameZGetUserNamer-   ZLookupAccountName)Zaccount_namer   r   r	   r1   Ú  s    r1   )TT)r6   )r6   )r6   )N)0r   Z
__future__r   rF   r   r   rU   Zacme.magic_typingr   rƒ   r-   r;   r    rA   rB   rD   ÚImportErrorr   r   r   r   r   r%   r(   r+   r4   r5   r8   rM   rN   rO   rX   r\   rk   rv   r{   r|   r‚   rj   r   r>   r‹   r   r'   rŒ   r*   r—   r1   r   r   r   r	   Ú<module>   s^   
!

F
!
 ##.
$	