ϪfMdZddlZddlZddlmZmZmZddlm Z ddl m Z ddl m Z ddlmZmZmZddlmZdd lmZmZmZdd lmZdd lmZdd lmZdd lmZddl m!Z!eZ"dZ#dZ$GddZ%e eGdde%Z&e eGddZ'dZ(e eGdde%e!Z)GddZ*GddZ+y)zE An implementation of the OpenSSH known_hosts database. @since: 8.2 N)Error a2b_base64 b2a_base64)closing)sha1) implementer)HostKeyChanged InvalidEntryUserRejectedKey)IKnownHostEntry) BadKeyErrorFingerprintFormatsKey)defer)Logger) nativeString) secureRandom) FancyEqMixinc4t|jS)z Encode a binary string as base64 with no trailing newline. @param s: The string to encode. @type s: L{bytes} @return: The base64-encoded string. @rtype: L{bytes} )rstrip)ss A/usr/lib/python3/dist-packages/twisted/conch/client/knownhosts.py _b64encoder s a=    c&|jdd}t|dk7r t|\}}}|jdd}t|dk(r|\}}|jd}n|d}d}t j t |}||||fS)a Extract common elements of base64 keys from an entry in a hosts file. @param string: A known hosts file entry (a single line). @type string: L{bytes} @return: a 4-tuple of hostname data (L{bytes}), ssh key type (L{bytes}), key (L{Key}), and comment (L{bytes} or L{None}). The hostname data is simply the beginning of the line up to the first occurrence of whitespace. @rtype: L{tuple} N r)splitlenr rstripr fromStringr) stringelements hostnameskeyType keyAndCommentsplitkey keyStringcommentkeys r_extractCommonr--s||D!$H 8}n(0%Iw ""4+H 8}% 7..'QK  ..I. /C gsG ++rceZdZdZdZdZy) _BaseEntrya Abstract base of both hashed and non-hashed entry objects, since they represent keys and key types the same way. @ivar keyType: The type of the key; either ssh-dss or ssh-rsa. @type keyType: L{bytes} @ivar publicKey: The server public key indicated by this line. @type publicKey: L{twisted.conch.ssh.keys.Key} @ivar comment: Trailing garbage after the key line. @type comment: L{bytes} c.||_||_||_yN)r' publicKeyr+)selfr'r2r+s r__init__z_BaseEntry.__init__Xs " rc |j|k(S)a Check to see if this entry matches a given key object. @param keyObject: A public key object to check. @type keyObject: L{Key} @return: C{True} if this entry's key matches C{keyObject}, C{False} otherwise. @rtype: L{bool} )r2)r3 keyObjects r matchesKeyz_BaseEntry.matchesKey]s~~**rN)__name__ __module__ __qualname____doc__r4r7rrr/r/Is  +rr/c>eZdZdZfdZedZdZdZxZ S) PlainEntryz A L{PlainEntry} is a representation of a plain-text entry in a known_hosts file. @ivar _hostnames: the list of all host-names associated with this entry. @type _hostnames: L{list} of L{bytes} c6||_t| |||yr1) _hostnamessuperr4)r3r&r'r2r+ __class__s rr4zPlainEntry.__init__us# )W5rcZt|\}}}}||jd|||}|S)a Parse a plain-text entry in a known_hosts file, and return a corresponding L{PlainEntry}. @param string: a space-separated string formatted like "hostname key-type base64-key-data comment". @type string: L{bytes} @raise DecodeError: if the key is not valid encoded as valid base64. @raise InvalidEntry: if the entry does not have the right number of elements and is therefore invalid. @raise BadKeyError: if the key, once decoded from base64, is not actually an SSH key. @return: an IKnownHostEntry representing the hostname and key in the input line. @rtype: L{PlainEntry} ,)r-r )clsr$r&r'r,r+r3s rr#zPlainEntry.fromStringys50,:&+A( 7C9??4('3@ rc`t|tr|jd}||jvS)aT Check to see if this entry matches a given hostname. @param hostname: A hostname or IP address literal to check against this entry. @type hostname: L{bytes} @return: C{True} if this entry is for the given hostname or IP address, C{False} otherwise. @rtype: L{bool} utf-8) isinstancestrencoder@r3hostnames r matchesHostzPlainEntry.matchesHosts+ h $w/H4??**rcdj|j|jt|jj g}|j |j|j dj|S)a Implement L{IKnownHostEntry.toString} by recording the comma-separated hostnames, key type, and base-64 encoded key. @return: The string representation of this entry, with unhashed hostname information. @rtype: L{bytes} rD )joinr@r'rr2blobr+appendr3fieldss rtoStringzPlainEntry.toStringsb IIdoo & LL t~~**, -  << # MM$,, 'yy  r) r8r9r:r;r4 classmethodr#rMrU __classcell__rBs@rr>r>ks+66+ !rr>c(eZdZdZdZdZdZdZy) UnparsedEntryz L{UnparsedEntry} is an entry in a L{KnownHostsFile} which can't actually be parsed; therefore it matches no keys and no hosts. c||_y)zv Create an unparsed entry from a line in a known_hosts file which cannot otherwise be parsed. N)_string)r3r$s rr4zUnparsedEntry.__init__s  rcyz' Always returns False. Fr<rKs rrMzUnparsedEntry.matchesHostrcyr^r<)r3r,s rr7zUnparsedEntry.matchesKeyr_rc8|jjdS)a Returns the input line, without its newline if one was given. @return: The string representation of this entry, almost exactly as was used to initialize this entry but without a trailing newline. @rtype: L{bytes} r)r\r"r3s rrUzUnparsedEntry.toStrings||""5))rN)r8r9r:r;r4rMr7rUr<rrrZrZs   *rrZctj|t}t|tr|j d}|j ||jS)z Return the SHA-1 HMAC hash of the given key and string. @param key: The HMAC key. @type key: L{bytes} @param string: The string to be hashed. @type string: L{bytes} @return: The keyed hash value. @rtype: L{bytes} ) digestmodrG)hmacHMACrrHrIrJupdatedigest)r,r$hashs r _hmacedStringrjsD 99SD )D&#w'KK ;;=rcFeZdZdZdZdZfdZedZdZ dZ xZ S) HashedEntrya A L{HashedEntry} is a representation of an entry in a known_hosts file where the hostname has been hashed and salted. @ivar _hostSalt: the salt to combine with a hostname for hashing. @ivar _hostHash: the hashed representation of the hostname. @cvar MAGIC: the 'hash magic' string used to identify a hashed line in a known_hosts file as opposed to a plaintext one. s|1|) _hostSalt _hostHashr'r2r+cD||_||_t| |||yr1)rmrnrAr4)r3hostSalthostHashr'r2r+rBs rr4zHashedEntry.__init__s"!! )W5rct|\}}}}|t|jdjd}t|dk7r t |\}}|t |t ||||} | S)a# Load a hashed entry from a string representing a line in a known_hosts file. @param string: A complete single line from a I{known_hosts} file, formatted as defined by OpenSSH. @type string: L{bytes} @raise DecodeError: if the key, the hostname, or the is not valid encoded as valid base64 @raise InvalidEntry: if the entry does not have the right number of elements and is therefore invalid, or the host/hash portion contains more items than just the host and hash. @raise BadKeyError: if the key, once decoded from base64, is not actually an SSH key. @return: The newly created L{HashedEntry} instance, initialized with the information from C{string}. N|r)r-r!MAGICr r r) rEr$stuffr'r,r+ saltAndHashrprqr3s rr#zHashedEntry.fromStringsw.(6f'=$wWC N,-33D9 { q . ((:h'H)=wWU rcjtjt|j||jS)a Implement L{IKnownHostEntry.matchesHost} to compare the hash of the input to the stored hash. @param hostname: A hostname or IP address literal to check against this entry. @type hostname: L{bytes} @return: C{True} if this entry is for the given hostname or IP address, C{False} otherwise. @rtype: L{bool} )recompare_digestrjrmrnrKs rrMzHashedEntry.matchesHost's+"" $..( 3T^^  rcZ|jdjt|jt|jgz|j t|j jg}|j|j|jdj|S)z Implement L{IKnownHostEntry.toString} by base64-encoding the salt, host hash, and key. @return: The string representation of this entry, with the hostname part hashed. @rtype: L{bytes} rsrO) rtrPrrmrnr'r2rQr+rRrSs rrUzHashedEntry.toString8s JJiiDNN3Z5OPQ R LL t~~**, -   << # MM$,, 'yy  r) r8r9r:r;rtcompareAttributesr4rVr#rMrUrWrXs@rrlrls8  EU6 < "!rrlcTeZdZdZdZedZdZdZdZ dZ dZ e d Z y ) KnownHostsFileaz A structured representation of an OpenSSH-format ~/.ssh/known_hosts file. @ivar _added: A list of L{IKnownHostEntry} providers which have been added to this instance in memory but not yet saved. @ivar _clobber: A flag indicating whether the current contents of the save path will be disregarded and potentially overwritten or not. If C{True}, this will be done. If C{False}, entries in the save path will be read and new entries will be saved by appending rather than overwriting. @type _clobber: L{bool} @ivar _savePath: See C{savePath} parameter of L{__init__}. c.g|_||_d|_y)a$ Create a new, empty KnownHostsFile. Unless you want to erase the current contents of C{savePath}, you want to use L{KnownHostsFile.fromPath} instead. @param savePath: The L{FilePath} to which to save new entries. @type savePath: L{FilePath} TN)_added _savePath_clobber)r3savePaths rr4zKnownHostsFile.__init__]s ! rc|jS)z< @see: C{savePath} parameter of L{__init__} )rrbs rrzKnownHostsFile.savePathks ~~rc#K|jD]}||jry |jj}|5|D]Q} |j t jrt j|}ntj|}|S dddy#t$rYywxYw#tttf$rt|}Y@wxYw#1swYyxYww)aK Iterate over the host entries in this file. @return: An iterable the elements of which provide L{IKnownHostEntry}. There is an element for each entry in the file as well as an element for each added but not yet saved entry. @rtype: iterable of L{IKnownHostEntry} providers N)r~rropenOSError startswithrlrtr#r> DecodeErrorr r rZ)r3entryfplines r iterentrieszKnownHostsFile.iterentriesrs[[ EK  ==  $$&B  0{'8'89 + 6 6t < * 5 5d ;       $\;?0)$/E0  se#C#B#C#C A B2C C## B/,C#.B//C#2CCCCC C#cDt|jt|j D]o\}}|j |s|j |j k(s6|j|ry|dkrd}d}n|dz}|j}t|||y)a Check for an entry with matching hostname and key. @param hostname: A hostname or IP address literal to check for. @type hostname: L{bytes} @param key: The public key to check for. @type key: L{Key} @return: C{True} if the given hostname and key are present in this file, C{False} if they are not. @rtype: L{bool} @raise HostKeyChanged: if the host key found for the given hostname does not match the given key. TrNrF) enumeraterr!r~rMr'sshTyper7rr )r3rLr,lineidxrrpaths r hasHostKeyzKnownHostsFile.hasHostKeys"((8(8(:S=M.gotHasKey..promptResponses9#6C0 '-//rECECDSAzThe authenticity of host 'z (z)' can't be established. z key fingerprint is SHA256:)formatz9. Are you sure you want to continue connecting (yes/no)? )rwarntyperrr fingerprintr SHA256_BASE64promptrJsysgetdefaultencoding addCallback) resultrkeytyperproceedrLrr,r3uis r gotHasKeyz/KnownHostsFile.verifyHostKey..gotHasKeysr3/GG88:|B'79 OOB,IIK 0((*d?%G%X.$R(/A/O/OP ))FMM#2H2H2J$KL**>::r)rexecuterr)r3rrLrr,hhkrs````` r verifyHostKeyzKnownHostsFile.verifyHostKeys7(mmDOOXs;( ;( ;Ty))rctd}|j}t|t||||d}|jj ||S)a Add a new L{HashedEntry} to the key database. Note that you still need to call L{KnownHostsFile.save} if you wish these changes to be persisted. @param hostname: A hostname or IP address literal to associate with the new entry. @type hostname: L{bytes} @param key: The public key to associate with the new entry. @type key: L{Key} @return: The L{HashedEntry} that was added. @rtype: L{HashedEntry} N)rrrlrjr~rR)r3rLr,saltr'rs rrzKnownHostsFile.addHostKeysI"B++-D-h"?#tT 5! rc |jj}|js|j|jrd}nd}|jj |5}|j rP|jdj|j Dcgc]}|jc}dzg|_dddd|_ycc}w#1swYd|_yxYw)zM Save this L{KnownHostsFile} to the path it was loaded from. wbabrNF) rparentisdirmakedirsrrr~writerPrU)r3pmode hostsFileObjrs rrzKnownHostsFile.save s NN ! ! #wwy JJL ==DD ^^  & !,{{""JJdkkJU 0JKeS!  !   K !  s'1CC/CCC)c$||}d|_|S)a Create a new L{KnownHostsFile}, potentially reading existing known hosts information from the given file. @param path: A path object to use for both reading contents from and later saving to. If no file exists at this path, it is not an error; a L{KnownHostsFile} with no entries is returned. @type path: L{FilePath} @return: A L{KnownHostsFile} initialized with entries from C{path}. @rtype: L{KnownHostsFile} F)r)rEr knownHostss rfromPathzKnownHostsFile.fromPath sY # rN)r8r9r:r;r4propertyrrrrrrrVrr<rrr|r|LsP  >B@*D.*rr|c"eZdZdZdZdZdZy) ConsoleUIz A UI object that can ask true/false questions and post notifications on the console, to be used during key verification. c||_y)aA @param opener: A no-argument callable which should open a console binary-mode file-like object to be used for reading and writing. This initializes the C{opener} attribute. @type opener: callable taking no arguments and returning a read/write file-like object N)opener)r3rs rr4zConsoleUI.__init__9s  rc^tjd}fd}|j|S)a Write the given text as a prompt to the console output, then read a result from the console input. @param text: Something to present to a user to solicit a yes or no response. @type text: L{bytes} @return: a L{Deferred} which fires with L{True} when the user answers 'yes' and L{False} when the user answers 'no'. It may errback if there were any I/O errors. Nc,tj5}|j |jj j }|dk(r dddy|dk(r dddy|jd\#1swYyxYw)NTsyessnoFsPlease type 'yes' or 'no': )rrrreadlinerlower)ignoredfanswerr3texts rbodyzConsoleUI.prompt..bodyRs' @1 ZZ\//1779F'# @ @  5$ @ @ >? @ @sAB )B 8B  B)rsucceedr)r3rdrs`` rrzConsoleUI.promptCs* MM$  @}}T""rc t|j5}|j|dddy#1swYyxYw#t$rtj dYywxYw)z Notify the user (non-interactively) of the provided text, by writing it to the console. @param text: Some information the user is to be made aware of. @type text: L{bytes} NzFailed to write to console)rrr Exceptionlogfailure)r3rrs rrzConsoleUI.warn`sR 6' 1     6 KK4 5 6s(A6A?AAA#"A#N)r8r9r:r;r4rrr<rrrr3s #: 6rr),r;rerbinasciirrrr contextlibrhashlibrzope.interfacertwisted.conch.errorr r r twisted.conch.interfacesr twisted.conch.ssh.keysr rrtwisted.internetrtwisted.loggerrtwisted.python.compatrtwisted.python.randbytesrtwisted.python.utilrrrr-r/r>rZrjrlr|rr<rrrs  AA&MM4GG"!.1, h !,8++D _I!I!I!X _!*!*!*H( _W!*lW!W!tddN9696r