3 cjY@shddlZddlZddlZddlmZddlmZmZmZddl m Z m Z ej e ZdZdLZdZdeedZGdd d ZGd!d"d"Zd#d$Zd%d&Zd'd(Zd)d*Zd+d,Zd-d.Zefd/d0ZdMd1d2ZGd3d4d4Zeed5d6d7Zeed5d8d9Z d:d;Z!ee"d<d=d>Z#d?d@Z$efdAdBZ%dCdDZ&efeeeefdEdFdGZ'dHdIZ(dJdKZ)dS)NN)suppress)ListSequenceTuple)subputilz/etc/ssh/sshd_configdsarsaecdsaed25519(ecdsa-sha2-nistp256-cert-v01@openssh.comecdsa-sha2-nistp256(ecdsa-sha2-nistp384-cert-v01@openssh.comecdsa-sha2-nistp384(ecdsa-sha2-nistp521-cert-v01@openssh.comecdsa-sha2-nistp521+sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"sk-ecdsa-sha2-nistp256@openssh.com#sk-ssh-ed25519-cert-v01@openssh.comsk-ssh-ed25519@openssh.comssh-dss-cert-v01@openssh.comssh-dss ssh-ed25519-cert-v01@openssh.com ssh-ed25519ssh-rsa-cert-v01@openssh.comssh-rsassh-xmss-cert-v01@openssh.comssh-xmss@openssh.comzno-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&eZdZdddZddZddZdS) AuthKeyLineNcCs"||_||_||_||_||_dS)N)base64commentoptionskeytypesource)selfr%r$r!r"r#r'/usr/lib/python3.6/ssh_util.py__init__Hs zAuthKeyLine.__init__cCs |jo |jS)N)r!r$)r&r'r'r(validQszAuthKeyLine.validcCsdg}|jr|j|j|jr(|j|j|jr:|j|j|jrL|j|j|sV|jSdj|SdS)N )r#appendr$r!r"r%join)r&toksr'r'r(__str__Ts    zAuthKeyLine.__str__)NNNN)__name__ __module__ __qualname__r)r*r/r'r'r'r(r Gs r c@s"eZdZdZddZdddZdS)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): cCsd}d}x~|t|kr|s&||d kr||}|dt|krH|d}P||d}|dkrn|dkrn|d}n|dkr|| }|d}q W|d|}||dj}||fS) 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. Frr+ \rN)r+r4)lenlstrip)r&entZquotediZcurcZnextcr#remainr'r'r(_extract_optionsxs     z"AuthKeyLineParser._extract_optionsNc Cs|jd}|jds |jdkr(t|Sdd}|j}y||\}}}WnZtk r|j|\} } |dkrt| }y|| \}}}Wntk rt|SXYnXt|||||dS)Nz #cSs^|jdd}t|dkr(tdt||dtkrDtd|dt|dkrZ|jd|S)NzTo few fields: %srzInvalid keytype %sr>)splitr7 TypeErrorVALID_KEY_TYPESr,)r9r.r'r'r( parse_ssh_keys     z.AuthKeyLineParser.parse..parse_ssh_key)r$r!r"r#)rstrip startswithstripr rAr<) r&Zsrc_liner#linerCr9r$r!r"Zkeyoptsr;r'r'r(parses* zAuthKeyLineParser.parse)N)r0r1r2__doc__r<rHr'r'r'r(r3dsr3c Csg}t}g}xp|D]h}y|}||kr>|j|q>W|||<q"Wx|D]}|j|qzWdd|D}|jddj|S)NcSsg|]}|jr|qSr')r*).0kr'r'r( sz*update_authorized_keys..rcSsg|] }t|qSr')str)rYbr'r'r(r[sr> )listranger7r*r!remover,r-)Z old_entrieskeysZto_addr:r9rZkeyrTr'r'r(update_authorized_keyss      rdcCs8tj|}| s|j r$td|tjj|jd|fS)Nz"Unable to get SSH info for user %rz.ssh)pwdgetpwnampw_dir RuntimeErrorrJrKr-)usernamepw_entr'r'r(users_ssh_infos  rkc Csxd|fd|fdf}|sd}|j}g}xL|D]D}x|D]\}}|j||}q6W|jdsftjj||}|j|q,W|S)Nz%hz%u%%%z%h/.ssh/authorized_keys/)rlrm)r@replacerErJrKr-r,) valueZhomedirriZmacrospathsZrenderedrKZmacroZfieldr'r'r(render_authorizedkeysfile_pathss  rrc Csd}|r d}tj|}|r@||kr@|dkr@tjd||||dStj|}||kr\|dM}n.tj|}tj|} || kr|dM}n|dM}||@d krtjd |||dS|r|d @d krtjd ||dSd S)aVCheck 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) iirootzXPath %s in %s must be own by user %s or by root, but instead is own by %s. Ignoring key.Fi8rzBPath %s in %s must be accessible by user %s, check its permissionszRPath %s in %s must not give writepermission to group or world users. Ignoring key.T)rZ get_ownerrRdebugZget_permissionsZ get_groupZget_user_groups) riZ current_path full_pathis_file strictmodesZminimal_permissionsownerZparent_permissionZ group_ownerZ user_groupsr'r'r(check_permissionssD        r|cCst|d}tdd}y|jddd}d}tjj|j}x|D]}|d|7}tjj|rrtjd|dStjj |rtjd|dS|j |sF||jkrqFtjj |st j |Pd} |j} |j} |j |jrd } |j} |j} tj|| d d t j|| | WdQRXt|||d|} | sFdSqFWtjj|sJtjj|rZtjd |dStjj |st j|dd d dt j||j|jt|||d |} | sdSWn6ttfk r} zt jtt| dSd} ~ XnXd S)Nr5rsrnr>z-Invalid directory. Symlink exists in path: %sFz*Invalid directory. File exists in path: %siiT)modeexist_okz%s is not a file!i)r}Zensure_dir_exists)rkr@rJrKdirnamergislinkrRrwrLrEexistsr SeLinuxGuardZpw_uidZpw_gidmakedirsZ chownbyidr|isdir write_filerOrPrQr\)rifilenamerzZ user_pwentZ root_pwentZ directoriesZ parent_folderZ home_folderZ directoryr}ZuidgidZ permissionser'r'r(check_create_pathJsb           rc Cs"t|\}}tjj|d}|}g}tj|ddny2t|}|jdd}|jdd} t||j |}Wn4t t fk r||d<tj t d t|dYnXWdQRXxXt|j|D]F\} } td | kd | k| jd j|j grt|| | dk} | r| }PqW||krt jd ||t|gfS)NZauthorized_keysT) recursiveZauthorizedkeysfilez%h/.ssh/authorized_keysrzyesrzhFailed extracting 'AuthorizedKeysFile' in SSH config from %r, using 'AuthorizedKeysFile' file %r insteadz%uz%hz{}/zAAuthorizedKeysFile has an user-specific authorized_keys, using %s)rkrJrKr-rrparse_ssh_config_mapgetrrrgrOrPrQrR DEF_SSHD_CFGzipr@anyrEformatrrwrX) riZ sshd_cfg_filessh_dirrjZdefault_authorizedkeys_fileZuser_authorizedkeys_fileZ auth_key_fnsZssh_cfgZ key_pathsrzZkey_path auth_key_fnZpermissions_okr'r'r(extract_authorized_keyssF   rc Cst}g}x$|D]}|j|jt||dqWt|\}}tjj|}tj |dd t ||} tj || ddWdQRXdS)N)r#T)r) preserve_mode) r3r,rHr\rrJrKrrrrdr) rbrir#rUZ key_entriesrZrZauth_key_entriesrcontentr'r'r(setup_user_keyss    rc@s*eZdZdddZeddZddZdS) SshdConfigLineNcCs||_||_||_dS)N)rG_keyrp)r&rGrZvr'r'r(r)szSshdConfigLine.__init__cCs|jdkrdS|jjS)N)rlower)r&r'r'r(rcs zSshdConfigLine.keycCs>|jdkrt|jSt|j}|jr6|dt|j7}|SdS)Nr+)rr\rGrp)r&rr'r'r(r/s    zSshdConfigLine.__str__)NN)r0r1r2r)propertyrcr/r'r'r'r(rs  r)returncCs"tjj|sgSttj|jS)N)rJrKrLparse_ssh_config_linesrrMrN)rWr'r'r(parse_ssh_configs rcCsg}x|D]}|j}| s&|jdr6|jt|q y|jdd\}}WnLtk ry|jdd\}}Wn"tk rtjd|w YnXYnX|jt|||q W|S)Nr=r5=z;sshd_config: option "%s" has no key/value pair, skipping it)rFrEr,rr@ ValueErrorrRrw)rTretrGrcvalr'r'r(rs$ rcCs:t|}|siSi}x |D]}|js&q|j||j<qW|S)N)rrcrp)rWrTrrGr'r'r(rs r)rWrc CsNtjj|sdSt|d*}x"|D]}|jd|dr"dSq"WWdQRXdS)NFrzInclude z .d/*.confT)rJrKrLopenrE)rWfrGr'r'r(_includes_dconf%s   rcCs^t|rZtjj|ds.tj|dddtjj|dd}tjj|sZtj|d|S)Nz.di)r}z50-cloud-init.confi) rrJrKrrZ ensure_dirr-rLZ ensure_file)rWr'r'r("_ensure_cloud_init_ssh_config_file/s  rcCsPt|}t|}t||d}|rDtj|djdd|Ddddt|dkS)zRead fname, and update if changes are necessary. @param updates: dictionary of desired values {Option: value} @return: boolean indicating if an update was done.)rTupdatesr^cSsg|] }t|qSr')r\)rYrGr'r'r(r[Esz%update_ssh_config..T)rr)rrupdate_ssh_config_linesrrr-r7)rrWrTchangedr'r'r(update_ssh_config:s rc Cst}g}tdd|jD}xt|ddD]v\}}|js>q.|j|kr.||j}||}|j||j|krtjd|||q.|j |tjd|||j|||_q.Wt |t |kr xN|j D]B\}}||krq|j ||j t d||tjdt |||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.cSsg|]}|j|fqSr')r)rYrZr'r'r(r[Usz+update_ssh_config_lines..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) setdictrb enumeratercaddrprRrwr,r7itemsr) rTrfoundrZcasemapr:rGrcrpr'r'r(rKs<       r)rTcCs>|sdSt|}dd|D}tj|dj|dddddS)Ncss |]\}}|d|VqdS)r+Nr')rYrZrr'r'r( }sz$append_ssh_config..r^ZabT)Zomoder)rrrr-)rTrWrr'r'r(append_ssh_configys rc Cspd}ttj tjddgddgd\}}WdQRXd}x2|jd D]$}|j|rD|t||jd SqDWdS) 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>Zsshdz-Vrr5)rcsNZOpenSSH_r^,)rrZProcessExecutionErrorr@rEr7find)err_prefixrGr'r'r(get_opensshd_versions  $ rc Csd}t}|dkrtjj|Sd|kr:|d|jd}n d|krV|d|jd}n|}ytjj|}|Sttfk rtjd|YnXdS)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` z9.0Npr+z Could not parse sshd version: %s) rrZVersionZfrom_strrrrArRZwarning)Zupstream_versionZ full_versionr'r'r(get_opensshd_upstream_versions  r)rr r r r r rrrrrrrrrrrrrrrr)N)*ZloggingrJre contextlibrZtypingrrrZ cloudinitrrZ getLoggerr0rRrrBZ_DISABLE_USER_SSH_EXITr\ZDISABLE_USER_OPTSr r3rXrdrkrrr|rrrrrrrboolrrrrrrrr'r'r'r( sh  YEO 9    .