U
    L¬÷dGY  ã                   @   sl  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
 d dl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d8dd„ZG dd „ d ƒZee d!œd"d#„Z ee d!œd$d%„Z!d&d'„ Z"ee#d(œd)d*„Z$d+d,„ Z%efd-d.„Z&d/d0„ Z'efeeeef  d1œd2d3„Z(d4d5„ Z)d6d7„ Z*dS )9é    N)Úsuppress)ÚListÚSequenceÚTuple)Úlog)ÚsubpÚutilz/etc/ssh/sshd_config)ZdsaZrsaZecdsaZ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-dss-cert-v01@openssh.comzssh-dssz 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__H   s
    zAuthKeyLine.__init__c                 C   s   | j o
| jS r   )r   r   ©r   r   r   r   ÚvalidQ   s    zAuthKeyLine.validc                 C   sd   g }| j r| | j ¡ | jr(| | j¡ | jr:| | j¡ | jrL| | j¡ |sV| jS d |¡S d S ©Nú )r   Úappendr   r   r   r   Újoin)r   Útoksr   r   r   Ú__str__T   s    zAuthKeyLine.__str__)NNNN)Ú__name__Ú
__module__Ú__qualname__r   r   r   r   r   r   r   r   G   s          ÿ
	r   c                   @   s"   e Zd ZdZdd„ Zddd„ZdS )ÚAuthKeyLineParsera‚  
    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, id_dsa.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 r„|s$|| dkr„|| }|d t |ƒkrF|d }q„||d  }|dkrl|dkrl|d }n|dkrz| }|d }q|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   ÚentZquotedÚiZcurcZnextcr   Úremainr   r   r   Ú_extract_optionsx   s     

z"AuthKeyLineParser._extract_optionsNc                 C   sÀ   |  d¡}| d¡s | ¡ dkr(t|ƒS dd„ }| ¡ }z||ƒ\}}}W nb tk
r¬   |  |¡\}	}
|d krt|	}z||
ƒ\}}}W n  tk
r¦   t|ƒ Y  Y S X Y nX t|||||dS )Nz
ú#Ú c                 S   s^   |   d d¡}t|ƒdk r(tdt|ƒ ƒ‚|d tkrDtd|d  ƒ‚t|ƒdkrZ| d¡ |S )Né   zTo few fields: %sr   zInvalid keytype %sr,   )Ú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   r/   r*   )r   Zsrc_liner   Úliner1   r'   r   r   r   Zkeyoptsr)   r   r   r   Úparse’   s,    
ûzAuthKeyLineParser.parse)N)r   r   r    Ú__doc__r*   r6   r   r   r   r   r!   d   s   r!   c              
   C   s|   g }t ƒ }g }| D ]d}z8tj |¡rLt |¡ ¡ }|D ]}| | |¡¡ q6W q t	t
fk
rt   t td|¡ Y qX q|S )NzError reading lines from %s)r!   ÚosÚpathÚisfiler   Ú	load_fileÚ
splitlinesr   r6   ÚIOErrorÚOSErrorÚlogexcÚLOG)ÚfnamesÚlinesÚparserÚcontentsÚfnamer5   r   r   r   Úparse_authorized_keys½   s    rF   c                 C   s¢   t dd„ |D ƒƒ}tdt| ƒƒD ]J}| | }| ¡ s6q |D ]&}|j|jkr:|}||kr:| |¡ q:|| |< q |D ]}|  |¡ qpdd„ | D ƒ}| d¡ d |¡S )Nc                 S   s   g | ]}|  ¡ r|‘qS r   )r   ©Ú.0Úkr   r   r   Ú
<listcomp>Î   s      z*update_authorized_keys.<locals>.<listcomp>r   c                 S   s   g | ]}t |ƒ‘qS r   ©Ústr)rH   Úbr   r   r   rJ   â   s     r,   Ú
)ÚlistÚranger%   r   r   Úremover   r   )Zold_entriesÚkeysZto_addr(   r'   rI   ÚkeyrB   r   r   r   Úupdate_authorized_keysÍ   s     

rT   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ÚRuntimeErrorr8   r9   r   )ÚusernameÚpw_entr   r   r   Úusers_ssh_infoé   s    

r[   c           	      C   sp   d|fd|fdf}| sd} |   ¡ }g }|D ]@}|D ]\}}| ||¡}q2| d¡s`tj ||¡}| |¡ q*|S )Nú%hú%u)z%%ú%ú%h/.ssh/authorized_keysú/)r.   Úreplacer3   r8   r9   r   r   )	ÚvalueZhomedirrY   ZmacrosÚpathsZrenderedr9   ZmacroZfieldr   r   r   Úrender_authorizedkeysfile_pathsð   s    
rd   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  | ¡}	||	kr‚|dM }n|dM }||@ d	krªt d
||| ¡ dS |rÌ|d@ d	krÌt 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   Z	get_ownerr@   ÚdebugZget_permissionsZ	get_groupZget_user_groups)
rY   Zcurrent_pathÚ	full_pathÚis_fileÚstrictmodesZminimal_permissionsÚownerZparent_permissionZgroup_ownerZuser_groupsr   r   r   Úcheck_permissions  sJ    
ú




ûüro   c              
   C   sø  t | ƒd }t dƒd }zš| d¡dd… }d}tj |j¡}|D ]ð}|d| 7 }tj |¡rtt d|¡  W dS tj 	|¡r”t d|¡  W dS | 
|¡sD||jkrªqDtj |¡st |¡P d	}	|j}
|j}| 
|j¡rðd
}	|j}
|j}tj||	dd t ||
|¡ W 5 Q R X t| ||d|ƒ}|sD W dS qDtj |¡sRtj |¡rdt d|¡ W dS tj |¡s–tj|dddd t ||j|j¡ t| ||d|ƒ}|s²W dS W n> ttfk
rò } zt tt|ƒ¡ W Y ¢dS d }~X Y nX dS )Nr#   re   r`   éÿÿÿÿr,   z-Invalid directory. Symlink exists in path: %sFz*Invalid directory. File exists in path: %séí  rf   T)ÚmodeÚexist_okz%s is not a file!é€  )rr   Zensure_dir_exists)r[   r.   r8   r9   ÚdirnamerW   Úislinkr@   rj   r:   r3   Úexistsr   ÚSeLinuxGuardZpw_uidZpw_gidÚmakedirsZ	chownbyidro   ÚisdirÚ
write_filer=   r>   r?   rL   )rY   Úfilenamerm   Z
user_pwentZ
root_pwentZdirectoriesZparent_folderZhome_folderZ	directoryrr   ZuidÚgidZpermissionsÚer   r   r   Úcheck_create_pathJ  s€    þ ÿÿþ    ÿ
    ÿ
r   c                 C   s   t | ƒ\}}tj |d¡}|}g }tj|ddn z2t|ƒ}| dd¡}| dd¡}	t||j	| ƒ}W n4 t
tfk
r˜   ||d< t td	t|d ¡ Y nX W 5 Q R X t| ¡ |ƒD ]H\}
}td
|
kd|
k| d |j	¡¡gƒr²t| ||	dkƒ}|r²|} qüq²||krt d|¡ |t|gƒfS )NZauthorized_keysT©Ú	recursiveZauthorizedkeysfiler_   rm   Zyesr   zhFailed extracting 'AuthorizedKeysFile' in SSH config from %r, using 'AuthorizedKeysFile' file %r insteadr]   r\   z{}/zAAuthorizedKeysFile has an user-specific authorized_keys, using %s)r[   r8   r9   r   r   rx   Úparse_ssh_config_mapÚgetrd   rW   r=   r>   r?   r@   ÚDEF_SSHD_CFGÚzipr.   Úanyr3   Úformatr   rj   rF   )rY   Zsshd_cfg_fileÚssh_dirrZ   Zdefault_authorizedkeys_fileZuser_authorizedkeys_fileZauth_key_fnsZssh_cfgZ	key_pathsrm   Zkey_pathÚauth_key_fnZ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 5 Q R X d S )N)r   Tr€   ©Úpreserve_mode)r!   r   r6   rL   rŠ   r8   r9   ru   r   rx   rT   r{   )
rR   rY   r   rC   Zkey_entriesrI   r‰   Z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   )r5   Ú_keyrb   )r   r5   rI   Úvr   r   r   r   â  s    zSshdConfigLine.__init__c                 C   s   | j d krd S | j  ¡ S r   )r   Úlowerr   r   r   r   rS   ç  s    
zSshdConfigLine.keyc                 C   s>   | j d krt| jƒS t| j ƒ}| jr6|dt| jƒ 7 }|S d S r   )r   rL   r5   rb   )r   r‘   r   r   r   r   î  s    


zSshdConfigLine.__str__)NN)r   r   r    r   ÚpropertyrS   r   r   r   r   r   r   á  s   

r   )Úreturnc                 C   s"   t j | ¡sg S tt | ¡ ¡ ƒS r   )r8   r9   r:   Úparse_ssh_config_linesr   r;   r<   ©rE   r   r   r   Úparse_ssh_configø  s    r—   c                 C   s°   g }| D ]¢}|  ¡ }|r"| d¡r2| t|ƒ¡ qz| d d¡\}}W nP tk
r–   z| dd¡\}}W n& tk
r   t d|¡ Y Y qY nX Y nX | t|||ƒ¡ q|S )Nr+   r#   ú=z;sshd_config: option "%s" has no key/value pair, skipping it)r4   r3   r   r   r.   Ú
ValueErrorr@   rj   )rB   Úretr5   rS   Úvalr   r   r   r•   þ  s&    ýr•   c                 C   s6   t | ƒ}|si S i }|D ]}|js$q|j||j< q|S r   )r—   rS   rb   )rE   rB   rš   r5   r   r   r   r‚     s    r‚   )rE   r”   c              	   C   sV   t j | ¡sdS t| dƒ2}|D ]&}| d| › d¡r  W 5 Q R £ dS q W 5 Q R X dS )NFÚrzInclude z	.d/*.confT)r8   r9   r:   Úopenr3   )rE   Úfr5   r   r   r   Ú_includes_dconf%  s    rŸ   c                 C   s^   t | ƒrZtj | › d¡s.tj| › ddd tj | › dd¡} tj | ¡sZt | d¡ | S )Nz.drq   )rr   z50-cloud-init.confrt   )	rŸ   r8   r9   rz   r   Z
ensure_dirr   r:   Zensure_filer–   r   r   r   Ú"_ensure_cloud_init_ssh_config_file/  s    r    c                 C   sP   t |ƒ}t|ƒ}t|| d}|rDt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.)rB   ÚupdatesrN   c                 S   s   g | ]}t |ƒ‘qS r   rK   )rH   r5   r   r   r   rJ   E  s     z%update_ssh_config.<locals>.<listcomp>Tr‹   r   )r    r—   Úupdate_ssh_config_linesr   r{   r   r%   )r¡   rE   rB   Úchangedr   r   r   Úupdate_ssh_config:  s    ýr¤   c           	      C   s  t ƒ }g }tdd„ | ¡ D ƒƒ}t| ddD ]v\}}|js<q,|j|kr,||j }|| }| |¡ |j|kr~t d|||¡ q,| 	|¡ t d|||j|¡ ||_q,t
|ƒt
|ƒkr| ¡ D ]B\}}||krÐq¾| 	|¡ |  	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’   rG   r   r   r   rJ   U  s     z+update_ssh_config_lines.<locals>.<listcomp>r#   )Ústartz$line %d: option %s already set to %sz#line %d: option %s updated %s -> %sr,   z line %d: option %s added with %s)ÚsetÚdictrR   Ú	enumeraterS   Úaddrb   r@   rj   r   r%   Úitemsr   )	rB   r¡   Úfoundr£   Zcasemapr(   r5   rS   rb   r   r   r   r¢   K  sN    



   ÿ
û
   ÿr¢   )rB   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   )rH   rI   r‘   r   r   r   Ú	<genexpr>}  s     z$append_ssh_config.<locals>.<genexpr>rN   ZabT)ZomoderŒ   )r    r   r{   r   )rB   rE   r   r   r   r   Úappend_ssh_configy  s    ür­   c               	   C   sp   d} t tjƒ  tjddgddgd\}} W 5 Q R X d}|  d¡D ](}| |¡rB|t|ƒ| d	¡…   S qBd
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.
    r,   Zsshdz-Vr   r#   )ZrcsZOpenSSH_rN   ú,N)r   r   ZProcessExecutionErrorr.   r3   r%   Úfind)ÚerrÚ_Úprefixr5   r   r   r   Úget_opensshd_version†  s    
$
r³   c               	   C   s–   d} t ƒ }|dkrtj | ¡S d|kr:|d| d¡… } n d|krV|d| d¡… } n|} ztj | ¡} | W S  ttfk
r   t d| ¡ Y nX dS )zäGet the upstream version of the OpenSSH sshd dameon 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   ZVersionZfrom_strr¯   r™   r/   r@   Zwarning)Zupstream_versionZfull_versionr   r   r   Úget_opensshd_upstream_versionš  s    rµ   )N)+r8   rU   Ú
contextlibr   Útypingr   r   r   Z	cloudinitr   Zloggingr   r   Z	getLoggerr   r@   r„   r0   Z_DISABLE_USER_SSH_EXITrL   ZDISABLE_USER_OPTSr   r!   rF   rT   r[   rd   ro   r   rŠ   rŽ   r   r—   r•   r‚   ÚboolrŸ   r    r¤   r¢   r­   r³   rµ   r   r   r   r   Ú<module>	   sH   
ýýÿYEO9

.