
Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­Â�Â­
<!DOCTYPE html>
<html>
3
ŠcjY  ã               @   sh  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 e jeƒZdZdLZ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/d0„ZdMd1d2„ZG d3d4„ d4ƒZee d5œd6d7„Zee d5œd8d9„Z d:d;„ Z!ee"d<œd=d>„Z#d?d@„ Z$efdAdB„Z%dCdD„ Z&efeeeef  dEœdFdG„Z'dHdI„ Z(dJdK„ Z)dS )Né    N)Úsuppress)ÚListÚSequenceÚTuple)ÚsubpÚutilz/etc/ssh/sshd_configÚdsaÚrsaÚecdsaÚed25519ú(ecdsa-sha2-nistp256-cert-v01@openssh.comúecdsa-sha2-nistp256ú(ecdsa-sha2-nistp384-cert-v01@openssh.comúecdsa-sha2-nistp384ú(ecdsa-sha2-nistp521-cert-v01@openssh.comúecdsa-sha2-nistp521ú+sk-ecdsa-sha2-nistp256-cert-v01@openssh.comú"sk-ecdsa-sha2-nistp256@openssh.comú#sk-ssh-ed25519-cert-v01@openssh.comúsk-ssh-ed25519@openssh.comússh-dss-cert-v01@openssh.comússh-dssú ssh-ed25519-cert-v01@openssh.comússh-ed25519ússh-rsa-cert-v01@openssh.comússh-rsaússh-xmss-cert-v01@openssh.comú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'   ú/usr/lib/python3.6/ssh_util.pyÚ__init__H   s
    zAuthKeyLine.__init__c             C   s   | j o
| jS )N)r!   r$   )r&   r'   r'   r(   ÚvalidQ   s    zAuthKeyLine.validc             C   sd   g }| j r|j| j ƒ | jr(|j| jƒ | jr:|j| jƒ | jrL|j| jƒ |sV| jS dj|ƒ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}x~|t |ƒk r†|s&|| d	kr†|| }|d t |ƒkrH|d }P ||d  }|dkrn|dkrn|d }n|dkr|| }|d }q
W |d|… }||d… jƒ }||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)r+   r4   )ÚlenÚlstrip)r&   ÚentZquotedÚiZcurcZnextcr#   Úremainr'   r'   r(   Ú_extract_optionsx   s     
z"AuthKeyLineParser._extract_optionsNc             C   s¸   |j dƒ}|jdƒs |jƒ dkr(t|ƒS dd„ }|jƒ }y||ƒ\}}}W nZ tk
r¤   | j|ƒ\}	}
|d krt|	}y||
ƒ\}}}W n tk
rž   t|ƒS X Y nX t|||||d�S )Nz
ú#Ú c             S   s^   | j d dƒ}t|ƒdk r(tdt|ƒ ƒ‚|d tkrDtd|d  ƒ‚t|ƒdkrZ|jdƒ |S )Né   zTo few fields: %sr   zInvalid keytype %sr>   )Úsplitr7   Ú	TypeErrorÚVALID_KEY_TYPESr,   )r9   r.   r'   r'   r(   Úparse_ssh_key˜   s    
z.AuthKeyLineParser.parse.<locals>.parse_ssh_key)r$   r!   r"   r#   )ÚrstripÚ
startswithÚstripr    rA   r<   )r&   Zsrc_liner#   ÚlinerC   r9   r$   r!   r"   Zkeyoptsr;   r'   r'   r(   Úparse’   s*    
zAuthKeyLineParser.parse)N)r0   r1   r2   Ú__doc__r<   rH   r'   r'   r'   r(   r3   d   s   r3   c             C   s„   g }t ƒ }g }xp| D ]h}y<tjj|ƒrRtj|ƒjƒ }x|D ]}|j|j|ƒƒ q:W W q t	t
fk
rz   tjtd|ƒ Y qX qW |S )NzError reading lines from %s)r3   ÚosÚpathÚisfiler   Ú	load_fileÚ
splitlinesr,   rH   ÚIOErrorÚOSErrorÚlogexcÚLOG)ÚfnamesÚlinesÚparserÚcontentsÚfnamerG   r'   r'   r(   Úparse_authorized_keys½   s    

rX   c             C   s®   t dd„ |D ƒƒ}x`tdt| ƒƒD ]N}| | }|jƒ s8q"x.|D ]&}|j|jkr>|}||kr>|j|ƒ q>W || |< q"W x|D ]}| j|ƒ qzW dd„ | D ƒ}|jdƒ dj|ƒS )Nc             S   s   g | ]}|j ƒ 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)rY   Úbr'   r'   r(   r[   â   s    r>   Ú
)ÚlistÚranger7   r*   r!   Úremover,   r-   )Zold_entriesÚkeysZto_addr:   r9   rZ   ÚkeyrT   r'   r'   r(   Úupdate_authorized_keysÍ   s     


rd   c             C   s8   t j| ƒ}| s|j r$td|  ƒ‚tjj|jdƒ|fS )Nz"Unable to get SSH info for user %rz.ssh)ÚpwdÚgetpwnamÚpw_dirÚRuntimeErrorrJ   rK   r-   )ÚusernameÚpw_entr'   r'   r(   Úusers_ssh_infoé   s    
rk   c       	      C   sx   d|fd|fdf}| sd} | j ƒ }g }xL|D ]D}x|D ]\}}|j||ƒ}q6W |jdƒsftjj||ƒ}|j|ƒ q,W |S )Nz%hz%uú%%ú%z%h/.ssh/authorized_keysú/)rl   rm   )r@   ÚreplacerE   rJ   rK   r-   r,   )	ÚvalueZhomedirri   ZmacrosÚpathsZrenderedrK   ZmacroZfieldr'   r'   r(   Úrender_authorizedkeysfile_pathsð   s    

rr   c       
      C   sÐ   d}|rd}t j|ƒ}|r@|| kr@|dkr@tjd||| |ƒ dS t j|ƒ}|| kr\|dM }n.t j|ƒ}t j| ƒ}	||	kr‚|dM }n|dM }||@ d	krªtjd
||| ƒ dS |rÌ|d@ d	krÌtj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.FiÀ  é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_ownerrR   ÚdebugZget_permissionsZ	get_groupZget_user_groups)
ri   Zcurrent_pathÚ	full_pathÚis_fileÚstrictmodesZminimal_permissionsÚownerZparent_permissionZgroup_ownerZuser_groupsr'   r'   r(   Úcheck_permissions  sD    





r|   c             C   sä  t | ƒd }t dƒd }�yŽ|jdƒdd… }d}tjj|jƒ}xì|D ]ä}|d| 7 }tjj|ƒrrtjd|ƒ dS tjj	|ƒrŽtjd|ƒ dS |j
|ƒsF||jkr¤qFtjj|ƒ�stj|ƒ�P d}	|j}
|j}|j
|jƒrêd	}	|j}
|j}tj||	d
d� tj||
|ƒ W d Q R X t| ||d|ƒ}|sFdS qFW tjj|ƒ�sJtjj|ƒ�rZtjd|ƒ dS tjj|ƒ�sŒtj|ddd
d� tj||j|jƒ t| ||d
|ƒ}|�s¦dS W n6 ttfk
�rÞ } ztjtt|ƒƒ dS d }~X nX d
S )Nr5   rs   rn   r>   z-Invalid directory. Symlink exists in path: %sFz*Invalid directory. File exists in path: %sií  iÀ  T)ÚmodeÚexist_okz%s is not a file!i€  )r}   Zensure_dir_existséÿÿÿÿ)rk   r@   rJ   rK   Údirnamerg   ÚislinkrR   rw   rL   rE   Úexistsr   ÚSeLinuxGuardZpw_uidZpw_gidÚmakedirsZ	chownbyidr|   ÚisdirÚ
write_filerO   rP   rQ   r\   )ri   Úfilenamerz   Z
user_pwentZ
root_pwentZdirectoriesZparent_folderZhome_folderZ	directoryr}   ZuidÚgidZpermissionsÚer'   r'   r(   Úcheck_create_pathJ  sb    


rŠ   c             C   s"  t | ƒ\}}tjj|dƒ}|}g }tj|dd��n y2t|ƒ}|jddƒ}|jddƒ}	t||j	| ƒ}W n4 t
tfk
r˜   ||d< tjtd	t|d ƒ Y nX W d Q R X xXt|jƒ |ƒD ]F\}
}td
|
kd|
k|jdj|j	ƒƒgƒr´t| ||	dkƒ}|r´|}P q´W ||k�rtjd|ƒ |t|gƒfS )NZauthorized_keysT)Ú	recursiveZauthorizedkeysfilez%h/.ssh/authorized_keysrz   Úyesr   zhFailed extracting 'AuthorizedKeysFile' in SSH config from %r, using 'AuthorizedKeysFile' file %r insteadz%uz%hz{}/zAAuthorizedKeysFile has an user-specific authorized_keys, using %s)rk   rJ   rK   r-   r   rƒ   Úparse_ssh_config_mapÚgetrr   rg   rO   rP   rQ   rR   ÚDEF_SSHD_CFGÚzipr@   ÚanyrE   ÚformatrŠ   rw   rX   )ri   Zsshd_cfg_fileÚssh_dirrj   Zdefault_authorizedkeys_fileZuser_authorizedkeys_fileZauth_key_fnsZssh_cfgZ	key_pathsrz   Zkey_pathÚauth_key_fnZpermissions_okr'   r'   r(   Úextract_authorized_keys™  sF    
r•   c       
      C   s€   t ƒ }g }x$| D ]}|j|jt|ƒ|d�ƒ qW t|ƒ\}}tjj|ƒ}tj	|dd��  t
||ƒ}	tj||	dd� W d Q R X d S )N)r#   T)r‹   )Úpreserve_mode)r3   r,   rH   r\   r•   rJ   rK   r€   r   rƒ   rd   r†   )
rb   ri   r#   rU   Zkey_entriesrZ   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 )N)rG   Ú_keyrp   )r&   rG   rZ   Úvr'   r'   r(   r)   â  s    zSshdConfigLine.__init__c             C   s   | j d krd S | j jƒ S )N)rš   Úlower)r&   r'   r'   r(   rc   ç  s    
zSshdConfigLine.keyc             C   s>   | j d krt| jƒS t| j ƒ}| jr6|dt| jƒ 7 }|S d S )Nr+   )rš   r\   rG   rp   )r&   r›   r'   r'   r(   r/   î  s    


zSshdConfigLine.__str__)NN)r0   r1   r2   r)   Úpropertyrc   r/   r'   r'   r'   r(   r™   á  s   
r™   )Úreturnc             C   s"   t jj| ƒsg S ttj| ƒjƒ ƒS )N)rJ   rK   rL   Úparse_ssh_config_linesr   rM   rN   )rW   r'   r'   r(   Úparse_ssh_configø  s    r    c             C   s²   g }x¨| D ] }|j ƒ }| s&|jdƒr6|jt|ƒƒ q
y|jd dƒ\}}W nL tk
r–   y|jddƒ\}}W n" tk
r�   tjd|ƒ w
Y nX Y nX |jt|||ƒƒ q
W |S )Nr=   r5   ú=z;sshd_config: option "%s" has no key/value pair, skipping it)rF   rE   r,   r™   r@   Ú
ValueErrorrR   rw   )rT   ÚretrG   rc   Úvalr'   r'   r(   rŸ   þ  s$    
rŸ   c             C   s:   t | ƒ}|si S i }x |D ]}|js&q|j||j< qW |S )N)r    rc   rp   )rW   rT   r£   rG   r'   r'   r(   r�     s    
r�   )rW   rž   c             C   sN   t jj| ƒsdS t| dƒ�*}x"|D ]}|jd| › d�ƒr"dS q"W W d Q R X dS )NFÚrzInclude z	.d/*.confT)rJ   rK   rL   ÚopenrE   )rW   ÚfrG   r'   r'   r(   Ú_includes_dconf%  s    
r¨   c             C   s^   t | ƒrZtjj| › d�ƒs.tj| › d�dd� tjj| › d�dƒ} tjj| ƒsZtj| dƒ | S )Nz.dií  )r}   z50-cloud-init.confi€  )	r¨   rJ   rK   r…   r   Z
ensure_dirr-   rL   Zensure_file)rW   r'   r'   r(   Ú"_ensure_cloud_init_ssh_config_file/  s    r©   c             C   sP   t |ƒ}t|ƒ}t|| d�}|rDtj|dj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.)rT   Úupdatesr^   c             S   s   g | ]}t |ƒ‘qS r'   )r\   )rY   rG   r'   r'   r(   r[   E  s    z%update_ssh_config.<locals>.<listcomp>T)r–   r   )r©   r    Úupdate_ssh_config_linesr   r†   r-   r7   )rª   rW   rT   Úchangedr'   r'   r(   Úupdate_ssh_config:  s    r­   c       	      C   s  t ƒ }g }tdd„ |jƒ D ƒƒ}x†t| dd�D ]v\}}|js>q.|j|kr.||j }|| }|j|ƒ |j|kr€tjd|||ƒ q.|j	|ƒ tjd|||j|ƒ ||_q.W t
|ƒt
|ƒk�r
xN|jƒ D ]B\}}||krÖqÄ|j	|ƒ | j	td||ƒƒ tjdt
| ƒ||ƒ qÄW |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 | ]}|j ƒ |f‘qS r'   )rœ   )rY   rZ   r'   r'   r(   r[   U  s    z+update_ssh_config_lines.<locals>.<listcomp>r5   )Ú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Údictrb   Ú	enumeraterc   Úaddrp   rR   rw   r,   r7   Úitemsr™   )	rT   rª   Úfoundr¬   Zcasemapr:   rG   rc   rp   r'   r'   r(   r«   K  s<    






r«   )rT   c             C   s>   | sd S t |ƒ}dd„ | D ƒ}tj|dj|ƒd ddd� d S )Nc             s   s    | ]\}}|› d |› �V  qdS )r+   Nr'   )rY   rZ   r›   r'   r'   r(   ú	<genexpr>}  s    z$append_ssh_config.<locals>.<genexpr>r^   ZabT)Zomoder–   )r©   r   r†   r-   )rT   rW   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 dQ R X d}x2| jd	ƒD ]$}|j|ƒrD|t|ƒ|jd
ƒ… S qDW 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.
    r>   Zsshdz-Vr   r5   )ÚrcsNZOpenSSH_r^   ú,)r   r   ZProcessExecutionErrorr@   rE   r7   Úfind)ÚerrÚ_ÚprefixrG   r'   r'   r(   Úget_opensshd_version†  s    
$
r½   c              C   s”   d} t ƒ }|dkrtjj| ƒS d|kr:|d|jdƒ… } n d|krV|d|jdƒ… } n|} ytjj| ƒ} | S  ttfk
rŽ   tj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¢   rA   rR   Zwarning)Zupstream_versionZfull_versionr'   r'   r(   Úget_opensshd_upstream_versionš  s    r¿   )r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )N)*ZloggingrJ   re   Ú
contextlibr   Ztypingr   r   r   Z	cloudinitr   r   Z	getLoggerr0   rR   r�   rB   Z_DISABLE_USER_SSH_EXITr\   ZDISABLE_USER_OPTSr    r3   rX   rd   rk   rr   r|   rŠ   r•   r˜   r™   r    rŸ   r�   Úboolr¨   r©   r­   r«   r¶   r½   r¿   r'   r'   r'   r(   Ú<module>	   sh   
                     YEO9

.