o bM@sPdZddlZddlZddlmZmZmZddlm Z ddl m Z ddl m Z ddlmZmZmZddlmZdd lmZmZmZdd lmZdd lmZdd lmZdd lmZddl m!Z!eZ"ddZ#ddZ$GdddZ%e eGddde%Z&e eGdddZ'ddZ(e eGddde%e!Z)GdddZ*Gdd d Z+dS)!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) FancyEqMixincCs t|S)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)srA/usr/lib/python3/dist-packages/twisted/conch/client/knownhosts.py _b64encode s rc Csz|dd}t|dkrt|\}}}|dd}t|dkr*|\}}|d}n|d}d}tt|}||||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 keyStringcommentkeyrrr_extractCommon-s      r*c@s eZdZdZddZddZdS) _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} cCs||_||_||_dSN)r$ publicKeyr()selfr$r-r(rrr__init__Xs z_BaseEntry.__init__cCs |j|kS)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} )r-)r. keyObjectrrr matchesKey]s z_BaseEntry.matchesKeyN)__name__ __module__ __qualname____doc__r/r1rrrrr+Is r+cs<eZdZdZfddZeddZddZdd ZZ 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} cs||_t|||dSr,) _hostnamessuperr/)r.r#r$r-r( __class__rrr/uszPlainEntry.__init__cCs(t|\}}}}||d|||}|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(r.rrrr yszPlainEntry.fromStringcCst|tr |d}||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) isinstancestrencoder7r.hostnamerrr matchesHosts  zPlainEntry.matchesHostcCs>d|j|jt|jg}|jdur||jd|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} r;N )joinr7r$rr-blobr(appendr.fieldsrrrtoStrings     zPlainEntry.toString) r2r3r4r5r/ classmethodr rCrJ __classcell__rrr9rr6ks  r6c@s0eZdZdZddZddZddZdd Zd S) UnparsedEntryz L{UnparsedEntry} is an entry in a L{KnownHostsFile} which can't actually be parsed; therefore it matches no keys and no hosts. cC ||_dS)zv Create an unparsed entry from a line in a known_hosts file which cannot otherwise be parsed. N)_string)r.r!rrrr/s zUnparsedEntry.__init__cCdSz' Always returns False. FrrArrrrCzUnparsedEntry.matchesHostcCrPrQr)r.r)rrrr1rRzUnparsedEntry.matchesKeycCs |jdS)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)rOrr.rrrrJs zUnparsedEntry.toStringN)r2r3r4r5r/rCr1rJrrrrrMs  rMcCs4tj|td}t|tr|d}|||S)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} ) digestmodr=)hmacHMACrr>r?r@updatedigest)r)r!hashrrr _hmacedStrings    rZcsDeZdZdZdZdZfddZeddZdd Z d d Z Z 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$r-r(cs ||_||_t|||dSr,)r\r]r8r/)r.hostSalthostHashr$r-r(r9rrr/szHashedEntry.__init__c Cs^t|\}}}}|t|jdd}t|dkrt|\}}|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*rMAGICrr r) r<r!stuffr$r)r( saltAndHashr^r_r.rrrr s zHashedEntry.fromStringcCstt|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} )rUcompare_digestrZr\r]rArrrrC's zHashedEntry.matchesHostcCsR|jdt|jt|jg|jt|jg}|jdur$| |jd|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} r`NrD) rarErr\r]r$r-rFr(rGrHrrrrJ8s     zHashedEntry.toString) r2r3r4r5racompareAttributesr/rKr rCrJrLrrr9rr[s   r[c@sXeZdZdZddZeddZddZdd Zd d Z d d Z ddZ e ddZ dS)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__}. cCsg|_||_d|_dS)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)r.savePathrrrr/]s  zKnownHostsFile.__init__cCs|jS)z< @see: C{savePath} parameter of L{__init__} )rhrSrrrrjkszKnownHostsFile.savePathc cs|jD]}|Vq|jrdSz|j}Wn ty YdSw|5|D])}z|tjr5t|}nt |}Wnt t t fyKt |}Ynw|Vq&WddS1s[wYdS)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)rgrirhopenOSError startswithr[rar r6 DecodeErrorr r rM)r.entryfplinerrr iterentriesrs.      "zKnownHostsFile.iterentriescCsxt|t|j D].\}}||r9|j|kr9||r#dS|dkr,d}d}n|d}|j}t |||q dS)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) enumeraterrrrgrCr$sshTyper1rhr)r.rBr)lineidxrorqpathrrr hasHostKeys  zKnownHostsFile.hasHostKeycs.tj}fdd}||S)a Verify the given host key for the given IP and host, asking for confirmation from, and notifying, the given UI about changes to this file. @param ui: The user interface to request an IP address from. @param hostname: The hostname that the user requested to connect to. @param ip: The string representation of the IP address that is actually being connected to. @param key: The public key of the server. @return: a L{Deferred} that fires with True when the key has been verified, or fires with an errback when the key either cannot be verified or has changed. @rtype: L{Deferred} cs|r!sdtf|Sfdd}}|dkr4d}dtt|jtjdf} | t }| |S)NzZWarning: Permanently added the %s host key for IP address '%s' to the list of known hosts.cs.|r|Str,) addHostKeysaver )response)rBipr)r.rrpromptResponses   zGKnownHostsFile.verifyHostKey..gotHasKey..promptResponseECECDSAzThe authenticity of host '%s (%s)' can't be established. %s key fingerprint is SHA256:%s. Are you sure you want to continue connecting (yes/no)? )format)rwwarntyperrxry fingerprintr SHA256_BASE64promptr@sysgetdefaultencoding addCallback)resultr|keytyperproceedrBr{r)r.uirr gotHasKeys0     z/KnownHostsFile.verifyHostKey..gotHasKey)r maybeDeferredrwr)r.rrBr{r)hhkrrrr verifyHostKeys *zKnownHostsFile.verifyHostKeycCs6td}|}t|t||||d}|j||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)rrtr[rZrgrG)r.rBr)saltr$rorrrrxs  zKnownHostsFile.addHostKeycCs|j}|s ||jrd}nd}|j|}|jr2|ddd|jDdg|_Wdn1ssz'KnownHostsFile.save..NF) rhparentisdirmakedirsrirkrgwriterE)r.pmode hostsFileObjrrrry s  zKnownHostsFile.savecCs||}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)ri)r<rv knownHostsrrrfromPath szKnownHostsFile.fromPathN)r2r3r4r5r/propertyrjrrrwrrxryrKrrrrrrfLs !Brfc@s(eZdZdZddZddZddZdS) ConsoleUIz A UI object that can ask true/false questions and post notifications on the console, to be used during key verification. cCrN)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)r.rrrrr/9s zConsoleUI.__init__cs"td}fdd}||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. Ncs~t/}| |}|dkr" WddS|dkr/ WddS|dq 1s8wYdS)NTsyessnoFsPlease type 'yes' or 'no': )rrrreadlinerlower)ignoredfanswerr.textrrbodyRs  zConsoleUI.prompt..body)rsucceedr)r.rdrrrrrCs  zConsoleUI.promptcCs`z t|}||WdWdS1swYWdSty/tdYdSw)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)r.rrrrrr`s & zConsoleUI.warnN)r2r3r4r5r/rrrrrrr3s  r),r5rUrbinasciirrnrr contextlibrhashlibrzope.interfacertwisted.conch.errorrr r twisted.conch.interfacesr twisted.conch.ssh.keysr r rtwisted.internetrtwisted.loggerrtwisted.python.compatrtwisted.python.randbytesrtwisted.python.utilrrrr*r+r6rMrZr[rfrrrrrs:          "L$Zh