U
    ¯AdÛR  ã                   @   sL  d dl Z d dlZd dlmZmZmZ d dlmZ d dl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d3d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.d/„ Z$efeeeef  d0œd1d2„Z%dS )4é    N)ÚListÚSequenceÚTuple)Úlog)Ú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__G   s
    zAuthKeyLine.__init__c                 C   s   | j o
| jS r
   )r   r   ©r   r   r   r   ÚvalidP   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__S   s    zAuthKeyLine.__str__)NNNN)Ú__name__Ú
__module__Ú__qualname__r   r   r   r   r   r   r   r	   F   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_optionsw   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   Úliner/   r%   r   r   r   Zkeyoptsr'   r   r   r   Úparse‘   s,    
ûzAuthKeyLineParser.parse)N)r   r   r   Ú__doc__r(   r4   r   r   r   r   r   c   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   r4   ÚIOErrorÚOSErrorÚlogexcÚLOG)ÚfnamesÚlinesÚparserÚcontentsÚfnamer3   r   r   r   Úparse_authorized_keys¼   s    rD   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)rF   Úbr   r   r   rH   á   s     r*   Ú
)ÚlistÚranger#   r   r   Úremover   r   )Zold_entriesÚkeysZto_addr&   r%   rG   Úkeyr@   r   r   r   Úupdate_authorized_keysÌ   s     

rR   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ÚRuntimeErrorr6   r7   r   )ÚusernameÚpw_entr   r   r   Úusers_ssh_infoè   s    

rY   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,   Úreplacer1   r6   r7   r   r   )	ÚvalueZhomedirrW   ZmacrosÚpathsZrenderedr7   ZmacroZfieldr   r   r   Úrender_authorizedkeysfile_pathsï   s    
rb   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)
rW   Zcurrent_pathÚ	full_pathÚis_fileÚstrictmodesZminimal_permissionsÚownerZparent_permissionZgroup_ownerZuser_groupsr   r   r   Úcheck_permissions  sJ    
ú




ûürm   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!   rc   r^   éÿÿÿÿr*   z-Invalid directory. Symlink exists in path: %sFz*Invalid directory. File exists in path: %séí  rd   T)ÚmodeÚexist_okz%s is not a file!é€  )rp   Zensure_dir_exists)rY   r,   r6   r7   ÚdirnamerU   Úislinkr>   rh   r8   r1   Úexistsr   ÚSeLinuxGuardÚpw_uidÚpw_gidÚmakedirsZ	chownbyidrm   ÚisdirÚ
write_filer;   r<   r=   rJ   )rW   Úfilenamerk   Z
user_pwentZ
root_pwentZdirectoriesZparent_folderZhome_folderZ	directoryrp   ZuidÚgidZpermissionsÚer   r   r   Úcheck_create_pathI  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]   rk   Zyesr   zhFailed extracting 'AuthorizedKeysFile' in SSH config from %r, using 'AuthorizedKeysFile' file %r insteadr[   rZ   z{}/zAAuthorizedKeysFile has an user-specific authorized_keys, using %s)rY   r6   r7   r   r   rv   Úparse_ssh_config_mapÚgetrb   rU   r;   r<   r=   r>   ÚDEF_SSHD_CFGÚzipr,   Úanyr1   Úformatr   rh   rD   )rW   Zsshd_cfg_fileÚssh_dirrX   Zdefault_authorizedkeys_fileZuser_authorizedkeys_fileZauth_key_fnsZssh_cfgZ	key_pathsrk   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   r4   rJ   rŠ   r6   r7   rs   r   rv   rR   r{   )
rP   rW   r   rA   Zkey_entriesrG   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
   )r3   Ú_keyr`   )r   r3   rG   Ú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   rQ   æ  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   rJ   r3   r`   )r   r‘   r   r   r   r   í  s    


zSshdConfigLine.__str__)NN)r   r   r   r   ÚpropertyrQ   r   r   r   r   r   r   à  s   

r   )Úreturnc                 C   s"   t j | ¡sg S tt | ¡ ¡ ƒS r
   )r6   r7   r8   Úparse_ssh_config_linesr   r9   r:   ©rC   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)r2   r1   r   r   r,   Ú
ValueErrorr>   rh   )r@   Úretr3   rQ   Úvalr   r   r   r•   ý  s&    ýr•   c                 C   s6   t | ƒ}|si S i }|D ]}|js$q|j||j< q|S r
   )r—   rQ   r`   )rC   r@   rš   r3   r   r   r   r‚     s    r‚   )rC   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)r6   r7   r8   Úopenr1   )rC   Úfr3   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.dro   )rp   z50-cloud-init.confrr   )	rŸ   r6   r7   rz   r   Z
ensure_dirr   r8   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.)r@   ÚupdatesrL   c                 S   s   g | ]}t |ƒ‘qS r   rI   )rF   r3   r   r   r   rH   D  s     z%update_ssh_config.<locals>.<listcomp>Tr‹   r   )r    r—   Úupdate_ssh_config_linesr   r{   r   r#   )r¡   rC   r@   Úchangedr   r   r   Úupdate_ssh_config9  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’   rE   r   r   r   rH   T  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ÚdictrP   Ú	enumeraterQ   Úaddr`   r>   rh   r   r#   Úitemsr   )	r@   r¡   Úfoundr£   Zcasemapr&   r3   rQ   r`   r   r   r   r¢   J  sN    



   ÿ
û
   ÿr¢   )r@   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   )rF   rG   r‘   r   r   r   Ú	<genexpr>|  s     z$append_ssh_config.<locals>.<genexpr>rL   ZabT)ZomoderŒ   )r    r   r{   r   )r@   rC   r   r   r   r   Úappend_ssh_configx  s    ür­   )N)&r6   rS   Útypingr   r   r   Z	cloudinitr   Zloggingr   Z	getLoggerr   r>   r„   r.   Z_DISABLE_USER_SSH_EXITrJ   ZDISABLE_USER_OPTSr	   r   rD   rR   rY   rb   rm   r   rŠ   rŽ   r   r—   r•   r‚   ÚboolrŸ   r    r¤   r¢   r­   r   r   r   r   Ú<module>	   sB   
ýýÿYEO9

.