o
    x[hÛX  ã                   @   sn  d dl Z d dlZd dlZd dlmZ d dlmZmZmZ d dl	m
Z
mZmZ e  e¡ZdZdZdZdeeƒ d	 ZG d
d„ dƒZG dd„ dƒZdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zdd„ Zefdd„Zd7dd„ZG dd„ dƒZd ee fd!d"„Z d ee fd#d$„Z!d%d&„ Z"d'ed e#fd(d)„Z$d*d+„ Z%efd,d-„Z&d.d/„ Z'efd0eeeef  fd1d2„Z(d3d4„ Z)d5d6„ Z*dS )8é    N)Úsuppress)ÚListÚSequenceÚTuple)Ú	lifecycleÚsubpÚutilz/etc/ssh/sshd_config)ÚrsaÚecdsaÚed25519z(ecdsa-sha2-nistp256-cert-v01@openssh.comzecdsa-sha2-nistp256z(ecdsa-sha2-nistp384-cert-v01@openssh.comzecdsa-sha2-nistp384z(ecdsa-sha2-nistp521-cert-v01@openssh.comzecdsa-sha2-nistp521z+sk-ecdsa-sha2-nistp256-cert-v01@openssh.comz"sk-ecdsa-sha2-nistp256@openssh.comz#sk-ssh-ed25519-cert-v01@openssh.comzsk-ssh-ed25519@openssh.comz ssh-ed25519-cert-v01@openssh.comzssh-ed25519zssh-rsa-cert-v01@openssh.comzssh-rsazssh-xmss-cert-v01@openssh.comzssh-xmss@openssh.coméŽ   z§no-port-forwarding,no-agent-forwarding,no-X11-forwarding,command="echo 'Please login as the user \"$USER\" rather than the user \"$DISABLE_USER\".';echo;sleep 10;exit ú"c                   @   s(   e Zd Z	ddd„Zdd„ Zdd„ ZdS )	ÚAuthKeyLineNc                 C   s"   || _ || _|| _|| _|| _d S ©N)Úbase64ÚcommentÚoptionsÚkeytypeÚsource)Úselfr   r   r   r   r   © r   ú4/usr/lib/python3/dist-packages/cloudinit/ssh_util.pyÚ__init__E   s
   
zAuthKeyLine.__init__c                 C   s   | j o| jS r   )r   r   ©r   r   r   r   ÚvalidN   s   zAuthKeyLine.validc                 C   s`   g }| j r| | j ¡ | jr| | j¡ | jr| | j¡ | jr&| | j¡ |s+| jS d |¡S ©Nú )r   Úappendr   r   r   r   Újoin)r   Útoksr   r   r   Ú__str__Q   s   
zAuthKeyLine.__str__)NNNN)Ú__name__Ú
__module__Ú__qualname__r   r   r    r   r   r   r   r   D   s
    
ÿ	r   c                   @   s"   e Zd ZdZdd„ Zddd„ZdS )ÚAuthKeyLineParserau  
    AUTHORIZED_KEYS FILE FORMAT
     AuthorizedKeysFile specifies the file containing public keys for public
     key authentication; if none is specified, the default is
     ~/.ssh/authorized_keys.  Each line of the file contains one key (empty
     (because of the size of the public key encoding) up to a limit of 8 kilo-
     bytes, which permits DSA keys up to 8 kilobits and RSA keys up to 16
     kilobits.  You don't want to type them in; instead, copy the
     identity.pub or the id_rsa.pub file and edit it.

     sshd enforces a minimum RSA key modulus size for protocol 1 and protocol
     2 keys of 768 bits.

     The options (if present) consist of comma-separated option specifica-
     tions.  No spaces are permitted, except within double quotes.  The fol-
     lowing option specifications are supported (note that option keywords are
     case-insensitive):
    c                 C   sÂ   d}d}|t |ƒk rO|s|| dvrO|| }|d t |ƒkr#|d }n,||d  }|dkr6|dkr6|d }n|dkr=| }|d }|t |ƒk rO|s|| dvs|d|… }||d…  ¡ }||fS )z×
        The options (if present) consist of comma-separated option specifica-
         tions.  No spaces are permitted, except within double quotes.
         Note that option keywords are case-insensitive.
        Fr   )r   ú	é   ú\r   N)ÚlenÚlstrip)r   ÚentÚquotedÚiÚcurcÚnextcr   Úremainr   r   r   Ú_extract_optionsu   s"   
öz"AuthKeyLineParser._extract_optionsNc                 C   s¼   |  d¡}| d¡s| ¡ dkrt|ƒS dd„ }| ¡ }z	||ƒ\}}}W n/ tyT   |  |¡\}	}
|d u r9|	}z	||
ƒ\}}}W n tyQ   t|ƒ Y  Y S w Y nw t|||||d�S )Nz
ú#Ú c                 S   s^   |   d d¡}t|ƒdk rtdt|ƒ ƒ‚|d tvr"td|d  ƒ‚t|ƒdkr-| d¡ |S )Né   zTo few fields: %sr   zInvalid keytype %sr2   )Úsplitr(   Ú	TypeErrorÚVALID_KEY_TYPESr   )r*   r   r   r   r   Úparse_ssh_key•   s   
z.AuthKeyLineParser.parse.<locals>.parse_ssh_key)r   r   r   r   )ÚrstripÚ
startswithÚstripr   r5   r0   )r   Úsrc_liner   Úliner7   r*   r   r   r   Úkeyoptsr/   r   r   r   Úparse�   s2   
ÿÿú
ûzAuthKeyLineParser.parser   )r!   r"   r#   Ú__doc__r0   r>   r   r   r   r   r$   a   s    r$   c              
   C   sx   g }t ƒ }g }| D ]0}ztj |¡r&t |¡ ¡ }|D ]
}| | |¡¡ qW q	 t	t
fy9   t td|¡ Y q	w |S )NzError reading lines from %s)r$   ÚosÚpathÚisfiler   Úload_text_fileÚ
splitlinesr   r>   ÚIOErrorÚOSErrorÚlogexcÚLOG)ÚfnamesÚlinesÚparserÚcontentsÚfnamer<   r   r   r   Úparse_authorized_keysº   s   €ÿrN   c                 C   s    t dd„ |D ƒƒ}tt| ƒƒD ]%}| | }| ¡ sq|D ]}|j|jkr/|}||v r/| |¡ q|| |< q|D ]}|  |¡ q7dd„ | D ƒ}| d¡ d |¡S )Nc                 S   s   g | ]}|  ¡ r|‘qS r   )r   ©Ú.0Úkr   r   r   Ú
<listcomp>Ë   ó    z*update_authorized_keys.<locals>.<listcomp>c                 S   ó   g | ]}t |ƒ‘qS r   ©Ústr)rP   Úbr   r   r   rR   ß   ó    r2   Ú
)ÚlistÚranger(   r   r   Úremover   r   )Úold_entriesÚkeysÚto_addr,   r*   rQ   ÚkeyrJ   r   r   r   Úupdate_authorized_keysÊ   s"   
€


ra   c                 C   s4   t  | ¡}|r
|jstd|  ƒ‚tj |jd¡|fS )Nz"Unable to get SSH info for user %rz.ssh)ÚpwdÚgetpwnamÚpw_dirÚRuntimeErrorr@   rA   r   )ÚusernameÚpw_entr   r   r   Úusers_ssh_infoæ   s   

rh   c           	      C   sp   d|fd|fdf}| sd} |   ¡ }g }|D ] }|D ]
\}}| ||¡}q| d¡s0tj ||¡}| |¡ q|S )Nú%hú%u)z%%ú%ú%h/.ssh/authorized_keysú/)r4   Úreplacer9   r@   rA   r   r   )	ÚvalueÚhomedirrf   ÚmacrosÚpathsÚrenderedrA   ÚmacroÚfieldr   r   r   Úrender_authorizedkeysfile_pathsí   s   
rv   c           
      C   sÌ   d}|rd}t  |¡}|r || kr |dkr t d||| |¡ dS t  |¡}|| kr.|dM }nt  |¡}t  | ¡}	||	v rA|dM }n|dM }||@ d	krUt d
||| ¡ dS |rd|d@ rdt d||¡ dS dS )aV  Check if the file/folder in @current_path has the right permissions.

    We need to check that:
    1. If StrictMode is enabled, the owner is either root or the user
    2. the user can access the file/folder, otherwise ssh won't use it
    3. If StrictMode is enabled, no write permission is given to group
       and world users (022)
    iÉ  i¤  ÚrootzXPath %s in %s must be own by user %s or by root, but instead is own by %s. Ignoring key.FéÀ  é8   é   r   zBPath %s in %s must be accessible by user %s, check its permissionsé   zRPath %s in %s must not give writepermission to group or world users. Ignoring key.T)r   Ú	get_ownerrH   ÚdebugÚget_permissionsÚ	get_groupÚget_user_groups)
rf   Úcurrent_pathÚ	full_pathÚis_fileÚstrictmodesÚminimal_permissionsÚownerÚparent_permissionÚgroup_ownerÚuser_groupsr   r   r   Úcheck_permissions  sJ   
ú




ûürŠ   c              
   C   sú  t | ƒd }t dƒd }zÓ| d¡dd… }d}tj |j¡}|D ]�}|d| 7 }tj |¡r9t d|¡  W dS tj 	|¡rIt d|¡  W dS | 
|¡sS||jkrTq!tj |¡s”t |¡�- d	}	|j}
|j}| 
|j¡rvd
}	|j}
|j}tj||	dd� t ||
|¡ W d   ƒ n1 s�w   Y  t| ||d|ƒ}|s¢ W dS q!tj |¡s¯tj |¡r¸t d|¡ W dS tj |¡sÐtj|dddd� t ||j|j¡ t| ||d|ƒ}|sÝW dS W dS  ttfyü } zt tt|ƒ¡ W Y d }~dS d }~ww )Nr&   rw   rm   éÿÿÿÿr2   z-Invalid directory. Symlink exists in path: %sFz*Invalid directory. File exists in path: %séí  rx   T)ÚmodeÚexist_okz%s is not a file!é€  )r�   Úensure_dir_exists)rh   r4   r@   rA   Údirnamerd   ÚislinkrH   r}   rB   r9   Úexistsr   ÚSeLinuxGuardÚpw_uidÚpw_gidÚmakedirsÚ	chownbyidrŠ   ÚisdirÚ
write_filerE   rF   rG   rV   )rf   Úfilenamer„   Ú
user_pwentÚ
root_pwentÚdirectoriesÚparent_folderÚhome_folderÚ	directoryr�   ÚuidÚgidÚpermissionsÚer   r   r   Úcheck_create_pathG  sv   þÿÿ
÷
ÿÿ
ÿÿü€þr¦   c                 C   s0  t | ƒ\}}tj |d¡}|}g }tj|dd��; zt|ƒ}| dd¡}| dd¡}	t||j	| ƒ}W n t
tfyK   ||d< t td	t|d ¡ Y nw W d   ƒ n1 sVw   Y  t| ¡ |ƒD ]$\}
}td
|
v d|
v | d |j	¡¡gƒr†t| ||	dkƒ}|r†|} nqb||kr‘t d|¡ |t|gƒfS )NÚauthorized_keysT©Ú	recursiveÚauthorizedkeysfilerl   r„   Úyesr   zhFailed extracting 'AuthorizedKeysFile' in SSH config from %r, using 'AuthorizedKeysFile' file %r insteadrj   ri   z{}/zAAuthorizedKeysFile has an user-specific authorized_keys, using %s)rh   r@   rA   r   r   r”   Úparse_ssh_config_mapÚgetrv   rd   rE   rF   rG   rH   ÚDEF_SSHD_CFGÚzipr4   Úanyr9   Úformatr¦   r}   rN   )rf   Ússhd_cfg_fileÚssh_dirrg   Údefault_authorizedkeys_fileÚuser_authorizedkeys_fileÚauth_key_fnsÚssh_cfgÚ	key_pathsr„   Úkey_pathÚauth_key_fnÚpermissions_okr   r   r   Úextract_authorized_keys–  s^   ÿÿúý€õýÿ
ÿ€ýþr¼   c           
      C   s’   t ƒ }g }| D ]}| |jt|ƒ|d�¡ qt|ƒ\}}tj |¡}tj	|dd�� t
||ƒ}	tj||	dd� W d   ƒ d S 1 sBw   Y  d S )N)r   Tr¨   ©Úpreserve_mode)r$   r   r>   rV   r¼   r@   rA   r‘   r   r”   ra   rš   )
r^   rf   r   rK   Úkey_entriesrQ   rº   Úauth_key_entriesr³   Úcontentr   r   r   Úsetup_user_keysÏ  s   
"þrÂ   c                   @   s*   e Zd Zddd„Zedd„ ƒZdd„ ZdS )	ÚSshdConfigLineNc                 C   s   || _ || _|| _d S r   )r<   Ú_keyro   )r   r<   rQ   Úvr   r   r   r   ß  s   
zSshdConfigLine.__init__c                 C   s   | j d u rd S | j  ¡ S r   )rÄ   Úlowerr   r   r   r   r`   ä  s   

zSshdConfigLine.keyc                 C   s:   | j d u r
t| jƒS t| j ƒ}| jr|dt| jƒ 7 }|S r   )rÄ   rV   r<   ro   )r   rÅ   r   r   r   r    ë  s   


zSshdConfigLine.__str__)NN)r!   r"   r#   r   Úpropertyr`   r    r   r   r   r   rÃ   Þ  s
    

rÃ   Úreturnc                 C   s"   t j | ¡sg S tt | ¡ ¡ ƒS r   )r@   rA   rB   Úparse_ssh_config_linesr   rC   rD   ©rM   r   r   r   Úparse_ssh_configõ  s   rË   c                 C   s¨   g }| D ]M}|  ¡ }|r| d¡r| t|ƒ¡ qz
| d d¡\}}W n$ tyG   z
| dd¡\}}W n tyD   t d|¡ Y Y qw Y nw | t|||ƒ¡ q|S )Nr1   r&   ú=z;sshd_config: option "%s" has no key/value pair, skipping it)r:   r9   r   rÃ   r4   Ú
ValueErrorrH   r}   )rJ   Úretr<   r`   Úvalr   r   r   rÉ   û  s,   ýúÿþ
rÉ   c                 C   s6   t | ƒ}|si S i }|D ]}|jsq|j||j< q|S r   )rË   r`   ro   )rM   rJ   rÎ   r<   r   r   r   r¬     s   r¬   rM   c                 C   s@   t j | ¡sdS t | ¡ ¡ D ]}| d| › d�¡r dS qdS )NFzInclude z	.d/*.confT)r@   rA   rB   r   rC   rD   r9   )rM   r<   r   r   r   Ú_includes_dconf"  s   ÿrÐ   c                 C   s^   t | ƒr-tj | › d�¡stj| › d�dd� tj | › d�d¡} tj | ¡s-t | d¡ | S )Nz.drŒ   )r�   z50-cloud-init.confr�   )	rÐ   r@   rA   r™   r   Ú
ensure_dirr   rB   Úensure_filerÊ   r   r   r   Ú"_ensure_cloud_init_ssh_config_file+  s   rÓ   c                 C   sP   t |ƒ}t|ƒ}t|| d�}|r"tj|d dd„ |D ƒ¡d dd� t|ƒdkS )z©Read fname, and update if changes are necessary.

    @param updates: dictionary of desired values {Option: value}
    @return: boolean indicating if an update was done.)rJ   ÚupdatesrY   c                 S   rT   r   rU   )rP   r<   r   r   r   rR   A  rX   z%update_ssh_config.<locals>.<listcomp>Tr½   r   )rÓ   rË   Úupdate_ssh_config_linesr   rš   r   r(   )rÔ   rM   rJ   Úchangedr   r   r   Úupdate_ssh_config6  s   ýr×   c           	      C   s  t ƒ }g }tdd„ | ¡ D ƒƒ}t| dd�D ];\}}|jsq|j|v rQ||j }|| }| |¡ |j|kr?t d|||¡ q| 	|¡ t d|||j|¡ ||_qt
|ƒt
|ƒkr€| ¡ D ]!\}}||v rgq^| 	|¡ |  	td||ƒ¡ t dt
| ƒ||¡ q^|S )	zðUpdate the SSH config lines per updates.

    @param lines: array of SshdConfigLine.  This array is updated in place.
    @param updates: dictionary of desired values {Option: value}
    @return: A list of keys in updates that were changed.c                 S   s   g | ]}|  ¡ |f‘qS r   )rÆ   rO   r   r   r   rR   Q  rS   z+update_ssh_config_lines.<locals>.<listcomp>r&   )Ústartz$line %d: option %s already set to %sz#line %d: option %s updated %s -> %sr2   z line %d: option %s added with %s)ÚsetÚdictr^   Ú	enumerater`   Úaddro   rH   r}   r   r(   ÚitemsrÃ   )	rJ   rÔ   ÚfoundrÖ   Úcasemapr,   r<   r`   ro   r   r   r   rÕ   G  sD   



ÿ
û€
ÿrÕ   rJ   c                 C   s>   | sd S t |ƒ}dd„ | D ƒ}tj|d |¡d ddd� d S )Nc                 s   s"   � | ]\}}|› d |› �V  qdS )r   Nr   )rP   rQ   rÅ   r   r   r   Ú	<genexpr>y  s   €  z$append_ssh_config.<locals>.<genexpr>rY   ÚabT)Úomoder¾   )rÓ   r   rš   r   )rJ   rM   rÁ   r   r   r   Úappend_ssh_configu  s   
ürã   c                  C   s„   d} t tjƒ� tjddgddgd�\}} W d  ƒ n1 sw   Y  d}|  d	¡D ]}| |¡r?|t|ƒ| d
¡…   S q+dS )zàGet the full version of the OpenSSH sshd daemon on the system.

    On an ubuntu system, this would look something like:
    1.2p1 Ubuntu-1ubuntu0.1

    If we can't find `sshd` or parse the version number, return None.
    r2   Ússhdz-Vr   r&   )ÚrcsNÚOpenSSH_rY   ú,)r   r   ÚProcessExecutionErrorr4   r9   r(   Úfind)ÚerrÚ_Úprefixr<   r   r   r   Úget_opensshd_version‚  s   
ÿ
ÿrí   c               	   C   s’   d} t ƒ }|du rtj | ¡S d|v r|d| d¡… } nd|v r+|d| d¡… } n|} z	tj | ¡} | W S  ttfyH   t d| ¡ Y dS w )zäGet the upstream version of the OpenSSH sshd daemon on the system.

    This will NOT include the portable number, so if the Ubuntu version looks
    like `1.2p1 Ubuntu-1ubuntu0.1`, then this function would return
    `1.2`
    z9.0NÚpr   z Could not parse sshd version: %s)	rí   r   ÚVersionÚfrom_strré   rÍ   r5   rH   Úwarning)Úupstream_versionÚfull_versionr   r   r   Úget_opensshd_upstream_version–  s   ÿrô   r   )+Úloggingr@   rb   Ú
contextlibr   Útypingr   r   r   Ú	cloudinitr   r   r   Ú	getLoggerr!   rH   r®   r6   Ú_DISABLE_USER_SSH_EXITrV   ÚDISABLE_USER_OPTSr   r$   rN   ra   rh   rv   rŠ   r¦   r¼   rÂ   rÃ   rË   rÉ   r¬   ÚboolrÐ   rÓ   r×   rÕ   rã   rí   rô   r   r   r   r   Ú<module>   sJ   
ýýÿYEO
9	.