
­­­­­­­­­­­­­­­­­­
<!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   zno-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rtjd
|||  dS |r|d@ d	krt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	|rtjd| dS |j
|sF||jkrqFtjj|stj|P d}	|j}
|j}|j
|jrd	}	|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|stj|ddd
d tj||j|j t| ||d
|}|sd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rt| ||	dk}|r|}P qW ||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 )zRead 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 }xt| ddD ]v\}}|js>q.|j|kr.||j }|| }|j| |j|krtjd||| q.|j	| tjd|||j| ||_q.W t
|t
|kr
xN|j D ]B\}}||krq|j	| | j	td|| tjdt
| || qW |S )	zUpdate 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 )zGet 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 )zGet 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

.