
    M/ew                     h   d Z ddlm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
 ddl	mZ ddl	mZ ddl	m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ZddlZddlZd
Z G d d      Z e       ZdededdfdZdedefdZededed   fd       ZdedededededdfdZ 	 d@dededededdf
dZ!dededefdZ"dedefdZ#dededefdZ$dAdedededefd Z%dAdededdfd!Z&dAdededdfd"Z'dededdfd#Z(dedefd$Z)d%edefd&Z*d'edefd(Z+d'edefd)Z,d*ed+edefd,Z-d-ed.edefd/Z.d'ed0edefd1Z/d'edefd2Z0dededdfd3Z1dBd4e
dedee   de
fd5Z2dedeeeeef   f   fd6Z3dededdfd7Z4dededdfd8Z5d9eeef   defd:Z6dededefd;Z7d<e
d=e
defd>Z8de
fd?Z9y# e$ r dZY Tw xY w)Cz;Compat module to handle files security on Windows and Linux    )absolute_import)contextmanagerN)Any)Dict)	Generator)List)OptionalFTc                       e Zd ZdZddZy)_WindowsUmaskz+Store the current umask to apply on WindowsNc                     d| _         y )N   )mask)selfs    ;/usr/lib/python3/dist-packages/certbot/compat/filesystem.py__init__z_WindowsUmask.__init__$   s	    	    )returnN)__name__
__module____qualname____doc__r    r   r   r   r   "   s
    5r   r   	file_pathmoder   c                 V    t         rt        j                  | |       yt        | |       y)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   s     r   r   r   +   s     
D!	4(r   r   c                 r    t         rt        j                  |       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_umasks     r   r"   r"   @   s.     xx~#((NNr   )NNNc              #   r   K   d}	 t        |       }d |t        |       yy# |t        |       w w xY ww)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   	old_umasks     r   
temp_umaskr'   Q   sF       $I$K	
 ) !9 ) !s   7$ 747srcdst	copy_user
copy_groupc                     t         rMt        j                  |       }|r|j                  nd}|r|j                  nd}t        j
                  |||       n|rt        | |       t        ||       y)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_ids           r   copy_ownership_and_apply_moder6   k   sW     "+%,,#-5<<2 	gx(	C%	#tr   c                 
   t         rct        j                  |       }|r|j                  nd}|r|j                  nd}t        j
                  |||       t        ||j                         y|rt        | |       t        | |       y)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/   r0   r1   r   st_moder2   _copy_win_mode)r(   r)   r*   r+   r3   r4   r5   s          r   copy_ownership_and_moder:      sc     "+%,,#-5<<2
gx(c5==!S)sC r   c                     t         r5t        j                  t        j                  |       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   r8   _check_win_moder    s     r   
check_moder>      s7     ||BGGI.6674??9d++r   c                     t         r4t        j                  |       j                  t        j                         k(  S t        j                  | t
        j                        }|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users      r   check_ownerrH      s_     wwy!((BIIK77 ,,Y8`8`aH..0D $&&r   c                 4    t        |       xr 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.
    )rH   r>   r    s     r   check_permissionsrJ      s     y!AjD&AAr   flagsc           	         t         rt        j                  | ||      S |t        j                  z  r4|t        j                  z  rt
        j                  nt
        j                  }t        j                         }|j                  }t               }t        ||t        j                        }|j                  |d       |j!                  d|d       d}	 t#        j$                  | t"        j&                  t"        j(                  t"        j*                  z  ||dd      }	 |r|jA                          	 t        j                  | |t        j                  z  t        j                  z        S t        j                  | |      }
tC        | |       |
S # t,        j.                  $ r}	|	j0                  t0        j2                  k(  r$t5        t6        j8                  |	j:                        |	j0                  t0        j<                  k(  r$t5        t6        j>                  |	j:                        |	d}	~	ww xY w# |r|jA                          w w xY w)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_ALWAYSrA   SECURITY_ATTRIBUTESSECURITY_DESCRIPTORrE   _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   rK   r   disposition
attributesrF   rG   daclhandleerrfds              r   rN   rN      s    wwy%.. rzz .3RYY->h))HDZDZ"668
11 "dD.*=*=> 	++D!4 	**1dA6	)))Y5K5K*3*C*CiF`F`*`*4k1dLF 
 wwy%"**"4ryy"@AA 
E	"B	)TI)  	 ||x999ellCLL99||x???ellCLL99I	  s&   AF
 
H&BH!!H&&H) )H>c                 b   t        d      }	 t        |d|z  z         t        r!t        j                  | |      t        |       S t        j                  }	 t        t        _        t        j                  | |      |t        _        t        |       S # |t        _        w xY w# t        |       w xY 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     )r"   r   r   makedirsmkdir)r   r   current_umaskorig_mkdir_fns       r   rq   rq     s     !HM
 	medl*+;;y$/ 	m 	% BH;;y$/$BHm %BHms)   ,B! B! $B :B! BB! !B.c                    t         rt        j                  | |      S t        j                         }|j
                  }t               }t        ||t        j                        }|j                  |d       |j                  d|d       	 t        j                  | |       y# t        j                  $ rT}|j                   t         j"                  k(  r0t%        t&        j(                  |j*                  | |j                         |d}~ww xY 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``
    FrM   r   N)r   r   rr   rA   rT   rU   rE   rV   r#   r   rW   rX   rY   CreateDirectoryr^   r_   r`   ERROR_ALREADY_EXISTSrb   rc   rd   re   )r   r   rj   rF   rG   rk   rm   s          r   rr   rr   :  s     xx	4((224J--HD$n&9&9:D''e4&&q$2!!)Z8    <<8888%,,iNN	s   B D2ADDc                     t        t        d      r t        t        d      | |       yt        j                  | |       y)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)   s     r   ry   ry   Y  s3     r9 	IsC( 			#sr   c                    | }t         st        j                  dk\  rZt        j                  j                  |       }t        j                  j                  |      rt        dj                  |            |S g }t        j                  j                  |       r| }t        j                  |       } t        j                  j                  |       s=t        j                  j                  t        j                  j                  |      |       } | |v rt        dj                  |            |j                  |        t        j                  j                  |       rt        j                  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_pathr   inspected_paths	link_paths        r   r   r   j  s
    M S%%/ww	*77>>$ ;BB=QRR!#O
''..
#	KK	*	ww}}Y'RWW__Y%?KI';BB=QRRy) ''..
# 77??9%%r   r   c                     t        j                  |       }t        s|j                  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   r   r   
startswithlen
ValueError)r   r   s     r   r   r     sI     ;;y!D3 4y3ABx
J
KKr   r   c                     t         rEt        j                  j                  |       xr$ t        j                  | 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   r   isfileaccessX_OK_win_is_executable)r   s    r   is_executabler     s9     ww~~d#@		$(@@d##r   c           	         t         rLt        t        j                  t	        j                  |       j
                        t        j                  z        S t        j                  | t        j                        }|j                         }t        |j                  t        j                  t        j                  t        j                  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   r8   S_IRWXOrA   rB   DACL_SECURITY_INFORMATIONGetSecurityDescriptorDaclGetEffectiveRightsFromAclTRUSTEE_IS_SIDTRUSTEE_IS_USERConvertStringSidToSid)r   rF   rk   s      r   has_world_permissionsr     s     DLL!6!67$,,FGG,,T=3Z3Z[H--/D..$33$44#99)D0   r   old_key	base_modec                    t         r{t        j                  t        j                  |       j                        t        j
                  t        j                  z  t        j                  z  t        j                  z  z  }||z  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   r8   S_IRGRPS_IWGRPS_IXGRPS_IROTH)r   r   old_modes      r   compute_private_key_moder     sa      LL!1!9!9:\\DLL04<<?$,,NP8## r   path1path2c                    t         r[t        j                  |       }t        j                  |      }|j                  |j                  f|j                  |j                  fk(  S t        j                  | t
        j                        }|j                         }t        j                  |t
        j                        }|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/   r0   rA   rB   rC   rD   )r   r   stats1stats2	security1user1	security2user2s           r   has_same_ownershipr     s     v}}-&--1OOO--e]5]5]^I002E--e]5]5]^I002EE>r   min_modec                    t         r't        j                  |       j                  }|||z  k(  S t	        |       } t        j                  | t
        j                  t
        j                  z        }|j                         }|j                         }t        ||      }t        |j                               D ]X  }|j                  |      }|d   }	|d   }|j                  t
        j                   t
        j"                  |d      }
|
|
|	z  k7  sX y y)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
    rM      r   FT)r   r   r.   r8   r   rA   rB   rC   r   rD   r   rV   rangeGetAceCountGetAcer   r   r   )r   r   r8   rF   rG   rk   min_daclindexmin_acer   effective_masks              r   has_min_permissionsr     s    ''$-'''H,,, D>D ,,m669`9``bH..0D--/DdH-Hx++-. //%( qzqz77(77(889
  ^d22" r   c                 d   t         j                  j                  |       syt        j                  | t        j
                        }|j                         }|j                  t        j                  t        j                  t               d      }|t        j                  z  t        j                  k(  S )NFr   )r   r   r   rA   rB   r   r   r   r   r   rE   ntsecurityconFILE_GENERIC_EXECUTE)r   rF   rk   r   s       r   r   r   ,  s    77>>$,,T=3Z3Z[H--/D))$33$44')+ D -4448Z8ZZZr   c                 
   t        |       } t        j                  | t        j                        }|j	                         }t        ||      }|j                  d|d       t        j                  | t        j                  |       y)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.
    rM   r   N)	r   rA   rB   rC   rD   rV   rX   SetFileSecurityr   )r   r   rF   rG   rk   s        r   r   r   <  so     #I,,Y8`8`aH..0D $%D &&q$2!!)]-T-TV^_r   user_sidc                 H   |r|d|z
  z  }t        |      }t        j                  d      }t        j                  d      }t        j                  d      }t        j                         }| ||fvr1t	        |d         }|r!|j                  t        j                  ||        t	        |d         }	|	r!|j                  t        j                  |	|       t	        dddd      }
|j                  t        j                  |
|       |j                  t        j                  |
|       |S )	Nrp   zS-1-5-18zS-1-5-32-544r   rG   allTreadwriteexecute)_analyze_moderA   r   ACL_generate_windows_flagsAddAccessAllowedAceACL_REVISION)r   r   r   analysissystemadminseveryonerk   
user_flagseverybody_flagsfull_permissionss              r   rV   rV   O  s   ut|$T"H
 00<F00@F229=H D '',Xf-=>
$$]%?%?XV .huo>O  !;!;_hW /tX\/]^]779I6R]779I6RKr   c                     | t         j                  z  | t         j                  z  | t         j                  z  d| t         j                  z  | t         j
                  z  | t         j                  z  ddS )Nr   )rG   r   )r.   S_IRUSRS_IWUSRS_IXUSRr   S_IWOTHS_IXOTH)r   s    r   r   r   s  sb     4<<'DLL(dll*
 4<<'DLL(dll*
 r   c                 8   t        |       } t        j                  | t        j                        }|j	                         }t        j                  |t        j                        }|j                  |d       t        j                  |t        j                  |       y NF)r   rA   rB   rC   rD   rW   r   )r(   r)   security_srcuser_srcsecurity_dsts        r   r2   r2     st    
3-C 00m6^6^_L668H 00m6^6^_L ++He<!!#}'O'OQ]^r   c                 :   t        |       } t        j                  | t        j                        }|j	                         }t        j                  |t        j                        }|j                  d|d       t        j                  |t        j                  |       y )NrM   r   )r   rA   rB   r   r   rX   r   )r(   r)   r   rk   r   s        r   r9   r9     sv    
3-C !00m6]6]^L113D 00m6]6]^L**1dA6!!#}'N'NP\]r   rights_descc                     d}| d   r|t         j                  z  }| d   r5|t         j                  t         j                  z  t         j                  z  z  }| d   r|t         j                  z  }|S )Nr   r   r   r   )r   FILE_GENERIC_READFILE_ALL_ACCESSr   )r   flags     r   r   r     sv    $ D6m5557}44&889&;;< = 9m888Kr   c                     t        |       } t        j                  | t        j                  t        j                  z        }|j                         }|j                         }|syt        ||      }t        ||      S r   )	r   rA   rB   rC   r   r   rD   rV   _compare_dacls)r   r   rF   rk   rG   ref_dacls         r   r=   r=     sw    #I,,Y8`8`/</V/V9W XH--/D ..0D  dD)H$))r   dacl1dacl2c                     t        | j                               D cg c]  }| j                  |       c}t        |j                               D cg c]  }|j                  |       c}k(  S c c}w c c}w )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.
    )r   r   r   )r   r   r   s      r   r   r     s`    
 /4E4E4E4G.HIUU\\% I.3E4E4E4G.HIUU\\% IJ KIIs   A.A3c                      dj                  t        j                         t        j                               } t	        j
                  d|       d   S )z=
    Return the pySID corresponding to the current user.
    z{0}\{1}Nr   )r   win32apiGetDomainNameGetUserNamerA   LookupAccountName)account_names    r   rE   rE     sB     $$X%;%;%=x?S?S?UVL **4>qAAr   )TT)rp   )N):r   
__future__r   
contextlibr   rc   r   r.   r   typingr   r   r   r   r	   r   r^   r   rQ   rY   rA   r`   r   ImportErrorr   r#   strintr   r"   r'   r   r6   r:   r>   rH   rJ   rN   rq   rr   ry   r   r   r   r   r   r   r   r   r   rV   r   r2   r9   r   r=   r   rE   r   r   r   <module>r     s   A & %  	  
      J  )S ) ) )*  " S Y'78  2s  C -1?CHLD HL! !3 !'+!@D!PT!2,# ,S ,T ,"'3 '4 '&	B 	BC 	BD 	BBC B B3 B3 BJ 3 4 @S   > 3 4 "& & &DL L LD$ $ $  *c c c *c # $ 2*c *S *T *Z[S [T [ `s `# `$ `&!S ! !8C= !C !H S$sCx.%8 9 _S _s _t _
^ 
^# 
^$ 
^c3h C >*s *# *$ *,K# Kc Kd KB3 BK  Js   F& &F10F1