o
    6­aês  ã                   @   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yG   dZY nw dZG dd„ dƒZeƒ Zd	ed
eddfdd„Zdedefdd„Zdeded
edededdfdd„Z	dRdededededdf
dd„Zd	ed
edefdd„Zd	edefdd„Zd	ed
edefdd„ZdSd	ed ed
edefd!d"„ZdSd	ed
eddfd#d$„ZdSd	ed
eddfd%d&„Z dededdfd'd(„Z!d	edefd)d*„Z"d+edefd,d-„Z#d.edefd/d0„Z$d.edefd1d2„Z%d3ed4edefd5d6„Z&d7ed8edefd9d:„Z'd.ed;edefd<d=„Z(d>d?„ Z)d@dA„ Z*dTdBdC„Z+dDdE„ Z,dFdG„ Z-dHdI„ Z.dJdK„ Z/dLdM„ Z0dNdO„ Z1dPdQ„ Z2dS )Uz;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   Ú	file_pathÚmodeÚreturnc                 C   s"   t r
t | |¡ dS 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©r   r   r   r   r	   r   &   s   r   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   Úprevious_umaskr   r   r	   r   ;   s
   	
r   ÚsrcÚdstÚ	copy_userÚ
copy_groupc                 C   sV   t rt | ¡}|r|jnd}|r|jnd}t |||¡ n|r$t| |ƒ 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   )r   r   r   r   r   ÚstatsÚuser_idÚgroup_idr   r   r	   Úcopy_ownership_and_apply_modeU   s   

r'   c                 C   sd   t r$t | ¡}|r|jnd}|r|jnd}t |||¡ t||jƒ dS |r+t| |ƒ 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_modev   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
   
r6   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.
    )r6   r-   r   r   r   r	   Úcheck_permissions³   s   	r7   éÿ  Úflagsc           
   
   C   s>  t r	t | ||¡S |tj@ r’|tj@ rtjntj}t 	¡ }|j
}tƒ }t||tjƒ}| |d¡ | d|d¡ d}zDzt | tjtjtj@ ||dd¡}W n) tjyu }	 z|	jtjkrbttj|	jƒ‚|	jtjkrottj|	jƒ‚|	‚d}	~	ww W |r}|  ¡  n|r…|  ¡  w w 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Úwin32conÚ
CREATE_NEWÚCREATE_ALWAYSr/   ÚSECURITY_ATTRIBUTESÚSECURITY_DESCRIPTORr3   Ú_generate_daclr   r   ÚSetSecurityDescriptorOwnerÚSetSecurityDescriptorDaclÚ	win32fileÚ
CreateFileÚGENERIC_READÚFILE_SHARE_READÚFILE_SHARE_WRITEÚ
pywintypesÚerrorÚwinerrorÚERROR_FILE_EXISTSÚOSErrorÚerrnoÚEEXISTÚstrerrorÚERROR_SHARING_VIOLATIONÚEACCESÚCloser   )
r   r9   r   ÚdispositionÚ
attributesr4   r5   ÚdaclÚhandleÚerrr   r   r	   r;   ¿   sF   


þ€ùý€ÿ
ÿ
r;   c                 C   sr   t dƒ}z/t |d|A B ƒ trt | |¡W t |ƒ S tj}ztt_t | |¡W |t_W t |ƒ S |t_w t |ƒ w )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   r8   )r   r   r   ÚmakedirsÚmkdir)r   r   Úcurrent_umaskÚorig_mkdir_fnr   r   r	   r[     s   	
÷
þ
r[   c              
   C   sž   t rt | |¡S t ¡ }|j}tƒ }t||tj	ƒ}| 
|d¡ | d|d¡ z	t | |¡ W dS  tjyN } z|jtjkrHttj|j| |jƒ‚|‚d}~ww )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:   r   N)r   r   r\   r/   rA   rB   r3   rC   r   r   rD   rE   rF   ÚCreateDirectoryrK   rL   rM   ÚERROR_ALREADY_EXISTSrO   rP   rQ   rR   )r   r   rW   r4   r5   rX   rZ   r   r   r	   r\   $  s"   	ù€ûr\   c                 C   s.   t tdƒrttdƒ| |ƒ dS 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	   ra   C  s   
ra   c                 C   s´   | }t s	tjdkrtj | ¡}tj |¡rtd |¡ƒ‚|S g }tj | ¡rT| }t 	| ¡} tj 
| ¡s>tj tj |¡| ¡} | |v rItd |¡ƒ‚| | ¡ tj | ¡s&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   Úoriginal_pathri   Úinspected_pathsÚ	link_pathr   r   r	   rj   T  s"   	

ù	rj   rv   c                 C   s<   t  | ¡}ts| d¡s|S t|ƒdk r|dd… S tdƒ‚)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   rn   r   Ú
startswithÚlenÚ
ValueError)rv   ri   r   r   r	   rn   v  s   
		rn   ri   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   ri   ÚisfileÚaccessÚX_OKÚ_win_is_executable)ri   r   r   r	   Úis_executable˜  s   r   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©ÚTrusteeFormÚTrusteeTypeÚ
Identifier)r   Úboolr   r+   r   r(   ÚS_IRWXOr/   r0   ÚDACL_SECURITY_INFORMATIONÚGetSecurityDescriptorDaclÚGetEffectiveRightsFromAclÚTRUSTEE_IS_SIDÚTRUSTEE_IS_USERÚConvertStringSidToSid)ri   r4   rX   r   r   r	   Úhas_world_permissions¦  s   
ýr   Úold_keyÚ	base_modec                 C   s:   t rt 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)rŽ   r   Úold_moder   r   r	   Úcompute_private_key_mode»  s   	ÿr•   Úpath1Úpath2c                 C   sd   t rt | ¡}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/   r0   r1   r2   )r–   r—   Ústats1Ústats2Ú	security1Úuser1Ú	security2Úuser2r   r   r	   Úhas_same_ownershipÐ  s   

rž   Úmin_modec                 C   sª   t rt | ¡j}|||B kS t| ƒ} t | tjtjB ¡}| 	¡ }| 
¡ }t||ƒ}t| ¡ ƒD ]#}| |¡}|d }	|d }| tjtj|dœ¡}
|
|
|	B krR 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
    r:   é   r   FT)r   r   r   r(   rj   r/   r0   r1   r‡   r2   rˆ   rC   ÚrangeÚGetAceCountÚGetAcer‰   rŠ   r‹   )ri   rŸ   r(   r4   r5   rX   Úmin_daclÚindexÚmin_acer   Ú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 )NFr   )r   ri   r{   r/   r0   r‡   rˆ   r‰   rŠ   r‹   r3   ÚntsecurityconÚFILE_GENERIC_EXECUTE)ri   r4   rX   r   r   r   r	   r~     s   ýr~   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.
    r:   r   N)	rj   r/   r0   r1   r2   rC   rE   ÚSetFileSecurityr‡   )r   r   r4   r5   rX   r   r   r	   r   &  s   
r   c                 C   s¾   |r|d| @ }t |ƒ}t d¡}t d¡}t d¡}t ¡ }| ||fvr5t|d ƒ}|r5| tj|| ¡ t|d ƒ}	|	rE| tj|	|¡ tddddœƒ}
| tj|
|¡ | tj|
|¡ |S )	Nr8   zS-1-5-18zS-1-5-32-544r€   r5   ÚallT©ÚreadÚwriteÚexecute)Ú_analyze_moder/   rŒ   ÚACLÚ_generate_windows_flagsÚAddAccessAllowedAceÚACL_REVISION)Úuser_sidr   r   ÚanalysisÚsystemÚadminsÚeveryonerX   Ú
user_flagsÚeverybody_flagsÚfull_permissionsr   r   r	   rC   9  s$   


rC   c                 C   s>   | t j@ | t j@ | t j@ dœ| t j@ | t j@ | t j@ dœdœS )Nr­   )r5   r¬   )r   ÚS_IRUSRÚS_IWUSRÚS_IXUSRr“   ÚS_IWOTHÚS_IXOTH)r   r   r   r	   r±   ]  s   ýýúr±   c                 C   sL   t | ƒ} t | tj¡}| ¡ }t |tj¡}| |d¡ t |tj|¡ d S ©NF)rj   r/   r0   r1   r2   rD   r«   )r   r   Úsecurity_srcÚuser_srcÚsecurity_dstr   r   r	   r#   l  s   r#   c                 C   sN   t | ƒ} t | tj¡}| ¡ }t |tj¡}| d|d¡ t |tj|¡ d S )Nr:   r   )rj   r/   r0   r‡   rˆ   rE   r«   )r   r   rÄ   rX   rÆ   r   r   r	   r)   {  s   r)   c                 C   sJ   d}| d r|t jB }| d r|t jt jA t jA B }| d r#|t jB }|S )Nr   r®   r¯   r°   )r©   ÚFILE_GENERIC_READÚFILE_ALL_ACCESSrª   )Úrights_descÚflagr   r   r	   r³   ˆ  s   
ÿþ
r³   c                 C   sH   t | ƒ} t | tjtjB ¡}| ¡ }| ¡ }|sdS t||ƒ}t||ƒS rÃ   )	rj   r/   r0   r1   r‡   rˆ   r2   rC   Ú_compare_dacls)r   r   r4   rX   r5   Ú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                    ó   g | ]}ˆ   |¡‘qS r   ©r£   ©Ú.0r¥   )Údacl1r   r	   Ú
<listcomp>Â  ó    z"_compare_dacls.<locals>.<listcomp>c                    rÍ   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   )rm   Úwin32apiÚGetDomainNameÚGetUserNamer/   ÚLookupAccountName)Úaccount_namer   r   r	   r3   Æ  s   r3   )TT)r8   )N)3r   Ú
__future__r   rP   r   r   rg   Útypingr   r©   r/   r>   rÖ   rF   rK   rM   ÚImportErrorr   r   r   ÚstrÚintr   r   r…   r'   r*   r-   r6   r7   r;   r[   r\   ra   rj   rn   r   r   r•   rž   r¨   r~   r   rC   r±   r#   r)   r³   r,   rË   r3   r   r   r   r	   Ú<module>   s|    ÿÿÿ
ÿ"ÿÿÿ
ÿE ""-
$	