Exemple #1
0
    def sign_ssh_data(self, data):
        key = dsa.DSAPrivateNumbers(
            x=self.x,
            public_numbers=dsa.DSAPublicNumbers(
                y=self.y,
                parameter_numbers=dsa.DSAParameterNumbers(
                    p=self.p,
                    q=self.q,
                    g=self.g
                )
            )
        ).private_key(backend=default_backend())
        signer = key.signer(hashes.SHA1())
        signer.update(data)
        r, s = decode_dss_signature(signer.finalize())

        m = Message()
        m.add_string('ssh-dss')
        # apparently, in rare cases, r or s may be shorter than 20 bytes!
        rstr = util.deflate_long(r, 0)
        sstr = util.deflate_long(s, 0)
        if len(rstr) < 20:
            rstr = zero_byte * (20 - len(rstr)) + rstr
        if len(sstr) < 20:
            sstr = zero_byte * (20 - len(sstr)) + sstr
        m.add_string(rstr + sstr)
        return m
Exemple #2
0
 def __init__(self, msg=None, data=None, filename=None, password=None,
              vals=None, file_obj=None):
     self.p = None
     self.q = None
     self.g = None
     self.y = None
     self.x = None
     self.public_blob = None
     if file_obj is not None:
         self._from_private_key(file_obj, password)
         return
     if filename is not None:
         self._from_private_key_file(filename, password)
         return
     if (msg is None) and (data is not None):
         msg = Message(data)
     if vals is not None:
         self.p, self.q, self.g, self.y = vals
     else:
         self._check_type_and_load_cert(
             msg=msg,
             key_type='ssh-dss',
             cert_type='*****@*****.**',
         )
         self.p = msg.get_mpint()
         self.q = msg.get_mpint()
         self.g = msg.get_mpint()
         self.y = msg.get_mpint()
     self.size = util.bit_length(self.p)
Exemple #3
0
def generate_key_pair(request):
    """
    Response to generate private/public RSA key pair
    """

    get_user(request, settings.ASTAKOS_AUTH_URL)

    if request.method != "POST":
        return http.HttpResponseNotAllowed(["POST"])

    if not SUPPORT_GENERATE_KEYS:
        raise Exception("Application does not support ssh keys generation")

    if PublicKeyPair.user_limit_exceeded(request.user_uniq):
        return http.HttpResponseServerError("SSH keys limit exceeded")

    # generate RSA key
    from Crypto import Random
    Random.atfork()

    key = rsakey.RSA.generate(SSH_KEY_LENGTH)

    # get PEM string
    pem = exportKey(key, 'PEM')

    public_data = Message()
    public_data.add_string('ssh-rsa')
    public_data.add_mpint(key.key.e)
    public_data.add_mpint(key.key.n)

    # generate public content
    public = str("ssh-rsa %s" % base64.b64encode(str(public_data)))

    data = {'private': pem, 'public': public}
    return http.HttpResponse(json.dumps(data), mimetype="application/json")
Exemple #4
0
 def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None, filecontent=None):
     self.n = None
     self.e = None
     self.d = None
     self.p = None
     self.q = None
     if file_obj is not None:
         self._from_private_key(file_obj, password)
         return
     if filename is not None:
         self._from_private_key_file(filename, password)
         return
     if filecontent is not None:
         self._decode_key(base64.decodestring(filecontent))
         return
     if (msg is None) and (data is not None):
         msg = Message(data)
     if vals is not None:
         self.e, self.n = vals
     else:
         if msg is None:
             raise SSHException('Key object may not be empty')
         if msg.get_string() != 'ssh-rsa':
             raise SSHException('Invalid key')
         self.e = msg.get_mpint()
         self.n = msg.get_mpint()
     self.size = util.bit_length(self.n)
Exemple #5
0
    def __init__(self, msg=None, data=None, filename=None, password=None,
                 vals=None, file_obj=None, validate_point=True):
        self.verifying_key = None
        self.signing_key = None
        if file_obj is not None:
            self._from_private_key(file_obj, password)
            return
        if filename is not None:
            self._from_private_key_file(filename, password)
            return
        if (msg is None) and (data is not None):
            msg = Message(data)
        if vals is not None:
            self.signing_key, self.verifying_key = vals
            c_class = self.signing_key.curve.__class__
            self.ecdsa_curve = self._ECDSA_CURVES.get_by_curve_class(c_class)
        else:
            if msg is None:
                raise SSHException('Key object may not be empty')
            self.ecdsa_curve = self._ECDSA_CURVES.get_by_key_format_identifier(
                msg.get_text())
            if self.ecdsa_curve is None:
                raise SSHException('Invalid key')
            curvename = msg.get_text()
            if curvename != self.ecdsa_curve.nist_name:
                raise SSHException("Can't handle curve of type %s" % curvename)

            pointinfo = msg.get_binary()
            try:
                numbers = ec.EllipticCurvePublicNumbers.from_encoded_point(
                    self.ecdsa_curve.curve_class(), pointinfo
                )
            except ValueError:
                raise SSHException("Invalid public key")
            self.verifying_key = numbers.public_key(backend=default_backend())
Exemple #6
0
    def __init__(self, msg=None, data=None, filename=None, password=None,
                 vals=None, file_obj=None, validate_point=True):
        self.verifying_key = None
        self.signing_key = None
        if file_obj is not None:
            self._from_private_key(file_obj, password)
            return
        if filename is not None:
            self._from_private_key_file(filename, password)
            return
        if (msg is None) and (data is not None):
            msg = Message(data)
        if vals is not None:
            self.signing_key, self.verifying_key = vals
        else:
            if msg is None:
                raise SSHException('Key object may not be empty')
            if msg.get_text() != 'ecdsa-sha2-nistp256':
                raise SSHException('Invalid key')
            curvename = msg.get_text()
            if curvename != 'nistp256':
                raise SSHException("Can't handle curve of type %s" % curvename)

            pointinfo = msg.get_binary()
            if pointinfo[0:1] != four_byte:
                raise SSHException('Point compression is being used: %s' %
                                   binascii.hexlify(pointinfo))
            curve = ec.SECP256R1()
            numbers = ec.EllipticCurvePublicNumbers(
                x=inflate_long(pointinfo[1:1 + curve.key_size // 8], always_positive=True),
                y=inflate_long(pointinfo[1 + curve.key_size // 8:], always_positive=True),
                curve=curve
            )
            self.verifying_key = numbers.public_key(backend=default_backend())
        self.size = 256
Exemple #7
0
 def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
     self.p = None
     self.q = None
     self.g = None
     self.y = None
     self.x = None
     if file_obj is not None:
         self._from_private_key(file_obj, password)
         return
     if filename is not None:
         self._from_private_key_file(filename, password)
         return
     if (msg is None) and (data is not None):
         msg = Message(data)
     if vals is not None:
         self.p, self.q, self.g, self.y = vals
     else:
         if msg is None:
             raise SSHException('Key object may not be empty')
         if msg.get_text() != 'ssh-dss':
             raise SSHException('Invalid key')
         self.p = msg.get_mpint()
         self.q = msg.get_mpint()
         self.g = msg.get_mpint()
         self.y = msg.get_mpint()
     self.size = util.bit_length(self.p)
Exemple #8
0
 def sign_ssh_data(self, data):
     sig = self.signing_key.sign_deterministic(
         data, sigencode=self._sigencode, hashfunc=sha256)
     m = Message()
     m.add_string('ecdsa-sha2-nistp256')
     m.add_string(sig)
     return m
Exemple #9
0
 def _parse_kexdh_gex_request_old(self, m):
     # same as above, but without min_bits or max_bits (used by older
     # clients like putty)
     self.preferred_bits = m.get_int()
     # smoosh the user's preferred size into our own limits
     if self.preferred_bits > self.max_bits:
         self.preferred_bits = self.max_bits
     if self.preferred_bits < self.min_bits:
         self.preferred_bits = self.min_bits
     # generate prime
     pack = self.transport._get_modulus_pack()
     if pack is None:
         raise SSHException("Can't do server-side gex with no modulus pack")
     self.transport._log(
         DEBUG, "Picking p (~ {} bits)".format(self.preferred_bits)
     )
     self.g, self.p = pack.get_modulus(
         self.min_bits, self.preferred_bits, self.max_bits
     )
     m = Message()
     m.add_byte(c_MSG_KEXDH_GEX_GROUP)
     m.add_mpint(self.p)
     m.add_mpint(self.g)
     self.transport._send_message(m)
     self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
     self.old_style = True
Exemple #10
0
    def __init__(self, msg=None, data=None, filename=None, password=None,
                 file_obj=None):
        self.public_blob = None
        verifying_key = signing_key = None
        if msg is None and data is not None:
            msg = Message(data)
        if msg is not None:
            self._check_type_and_load_cert(
                msg=msg,
                key_type="ssh-ed25519",
                cert_type="*****@*****.**",
            )
            verifying_key = nacl.signing.VerifyKey(msg.get_binary())
        elif filename is not None:
            with open(filename, "r") as f:
                data = self._read_private_key("OPENSSH", f)
        elif file_obj is not None:
            data = self._read_private_key("OPENSSH", file_obj)

        if filename or file_obj:
            signing_key = self._parse_signing_key_data(data, password)

        if signing_key is None and verifying_key is None:
            raise ValueError("need a key")

        self._signing_key = signing_key
        self._verifying_key = verifying_key
Exemple #11
0
 def from_string(cls, string):
     """
     Create a public blob from a ``-cert.pub``-style string.
     """
     fields = string.split(None, 2)
     if len(fields) < 2:
         msg = "Not enough fields for public blob: {}"
         raise ValueError(msg.format(fields))
     key_type = fields[0]
     key_blob = decodebytes(b(fields[1]))
     try:
         comment = fields[2].strip()
     except IndexError:
         comment = None
     # Verify that the blob message first (string) field matches the
     # key_type
     m = Message(key_blob)
     blob_type = m.get_text()
     if blob_type != key_type:
         deets = "key type={!r}, but blob type={!r}".format(
             key_type, blob_type
         )
         raise ValueError("Invalid PublicBlob contents: {}".format(deets))
     # All good? All good.
     return cls(type_=key_type, blob=key_blob, comment=comment)
Exemple #12
0
    def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None):
        self.verifying_key = None
        self.signing_key = None
        if file_obj is not None:
            self._from_private_key(file_obj, password)
            return
        if filename is not None:
            self._from_private_key_file(filename, password)
            return
        if (msg is None) and (data is not None):
            msg = Message(data)
        if vals is not None:
            self.verifying_key, self.signing_key = vals
        else:
            if msg is None:
                raise SSHException('Key object may not be empty')
            if msg.get_text() != 'ecdsa-sha2-nistp256':
                raise SSHException('Invalid key')
            curvename = msg.get_text()
            if curvename != 'nistp256':
                raise SSHException("Can't handle curve of type %s" % curvename)

            pointinfo = msg.get_binary()
            if pointinfo[0:1] != four_byte:
                raise SSHException('Point compression is being used: %s' %
                                   binascii.hexlify(pointinfo))
            self.verifying_key = VerifyingKey.from_string(pointinfo[1:],
                                                          curve=curves.NIST256p)
        self.size = 256
Exemple #13
0
 def _read_response(self, waitfor=None):
     while True:
         try:
             t, data = self._read_packet()
         except EOFError as e:
             raise SSHException('Server connection dropped: %s' % str(e))
         msg = Message(data)
         num = msg.get_int()
         if num not in self._expecting:
             # might be response for a file that was closed before responses came back
             self._log(DEBUG, 'Unexpected response #%d' % (num,))
             if waitfor is None:
                 # just doing a single check
                 break
             continue
         fileobj = self._expecting[num]
         del self._expecting[num]
         if num == waitfor:
             # synchronous
             if t == CMD_STATUS:
                 self._convert_status(msg)
             return t, msg
         if fileobj is not type(None):
             fileobj._async_response(t, msg, num)
         if waitfor is None:
             # just doing a single check
             break
     return None, None
Exemple #14
0
def sign_token(key_path, fingerprint, data):
    # from agent
    pkey = get_key_from_agent(fingerprint)
    if not pkey:
        # or from file (without passphrase)
        # assuming '.pub' file extension
        if not os.path.exists(key_path[:-4]):
            raise SignatureException('WrongKeyPath')
        try:
            pkey = RSAKey.from_private_key_file(key_path[:-4])
        except PasswordRequiredException:
            raise SignatureException('EncryptedKey')

    if not pkey:
        raise SignatureException('KeyNotFound')

    try:
        # paramiko is inconsistent here in that the agent's key
        # returns Message objects for 'sign_ssh_data' whereas RSAKey
        # objects returns byte strings.
        # Workaround: cast both return values to string and build a
        # new Message object
        s = str(pkey.sign_ssh_data(data))
        m = Message(s)
        m.rewind()
        if not m.get_string() == 'ssh-rsa':
            raise SignatureException('RSAKeyRequired')
        return base64.b64encode(m.get_string())
    except Exception:
        raise SignatureException('SignatureCreateFailure')
Exemple #15
0
 def _parse_kexdh_gex_request(self, m):
     minbits = m.get_int()
     preferredbits = m.get_int()
     maxbits = m.get_int()
     # smoosh the user's preferred size into our own limits
     if preferredbits > self.max_bits:
         preferredbits = self.max_bits
     if preferredbits < self.min_bits:
         preferredbits = self.min_bits
     # fix min/max if they're inconsistent.  technically, we could just pout
     # and hang up, but there's no harm in giving them the benefit of the
     # doubt and just picking a bitsize for them.
     if minbits > preferredbits:
         minbits = preferredbits
     if maxbits < preferredbits:
         maxbits = preferredbits
     # now save a copy
     self.min_bits = minbits
     self.preferred_bits = preferredbits
     self.max_bits = maxbits
     # generate prime
     pack = self.transport._get_modulus_pack()
     if pack is None:
         raise SSHException('Can\'t do server-side gex with no modulus pack')
     self.transport._log(DEBUG, 'Picking p (%d <= %d <= %d bits)' % (minbits, preferredbits, maxbits))
     self.g, self.p = pack.get_modulus(minbits, preferredbits, maxbits)
     m = Message()
     m.add_byte(chr(_MSG_KEXDH_GEX_GROUP))
     m.add_mpint(self.p)
     m.add_mpint(self.g)
     self.transport._send_message(m)
     self.transport._expect_packet(_MSG_KEXDH_GEX_INIT)
Exemple #16
0
    def __init__(
        self,
        msg=None,
        data=None,
        filename=None,
        password=None,
        vals=None,
        file_obj=None,
        validate_point=True,
    ):
        self.verifying_key = None
        self.signing_key = None
        self.public_blob = None
        if file_obj is not None:
            self._from_private_key(file_obj, password)
            return
        if filename is not None:
            self._from_private_key_file(filename, password)
            return
        if (msg is None) and (data is not None):
            msg = Message(data)
        if vals is not None:
            self.signing_key, self.verifying_key = vals
            c_class = self.signing_key.curve.__class__
            self.ecdsa_curve = self._ECDSA_CURVES.get_by_curve_class(c_class)
        else:
            # Must set ecdsa_curve first; subroutines called herein may need to
            # spit out our get_name(), which relies on this.
            key_type = msg.get_text()
            # But this also means we need to hand it a real key/curve
            # identifier, so strip out any cert business. (NOTE: could push
            # that into _ECDSACurveSet.get_by_key_format_identifier(), but it
            # feels more correct to do it here?)
            suffix = "*****@*****.**"
            if key_type.endswith(suffix):
                key_type = key_type[: -len(suffix)]
            self.ecdsa_curve = self._ECDSA_CURVES.get_by_key_format_identifier(
                key_type
            )
            key_types = self._ECDSA_CURVES.get_key_format_identifier_list()
            cert_types = [
                "{}[email protected]".format(x) for x in key_types
            ]
            self._check_type_and_load_cert(
                msg=msg, key_type=key_types, cert_type=cert_types
            )
            curvename = msg.get_text()
            if curvename != self.ecdsa_curve.nist_name:
                raise SSHException(
                    "Can't handle curve of type {}".format(curvename)
                )

            pointinfo = msg.get_binary()
            try:
                numbers = ec.EllipticCurvePublicNumbers.from_encoded_point(
                    self.ecdsa_curve.curve_class(), pointinfo
                )
            except ValueError:
                raise SSHException("Invalid public key")
            self.verifying_key = numbers.public_key(backend=default_backend())
Exemple #17
0
 def _parse_userauth_gssapi_token(self, m):
     client_token = m.get_string()
     # use the client token as input to establish a secure
     # context.
     sshgss = self.sshgss
     try:
         token = sshgss.ssh_accept_sec_context(
             self.gss_host, client_token, self.auth_username
         )
     except Exception as e:
         self.transport.saved_exception = e
         result = AUTH_FAILED
         self._restore_delegate_auth_handler()
         self._send_auth_result(self.auth_username, self.method, result)
         raise
     if token is not None:
         m = Message()
         m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
         m.add_string(token)
         self.transport._expected_packet = (
             MSG_USERAUTH_GSSAPI_TOKEN,
             MSG_USERAUTH_GSSAPI_MIC,
             MSG_USERAUTH_REQUEST,
         )
         self.transport._send_message(m)
Exemple #18
0
 def sign_ssh_data(self, rpool, data):
     digest = SHA256.new(data).digest()
     sig = self.signing_key.sign_digest(digest, entropy=rpool.read,
                                        sigencode=self._sigencode)
     m = Message()
     m.add_string('ecdsa-sha2-nistp256')
     m.add_string(sig)
     return m
Exemple #19
0
 def sign_ssh_data(self, rpool, data):
     digest = SHA.new(data).digest()
     rsa = RSA.construct((long(self.n), long(self.e), long(self.d)))
     sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), bytes())[0], 0)
     m = Message()
     m.add_string('ssh-rsa')
     m.add_string(sig)
     return m
Exemple #20
0
    def sign_ssh_data(self, data):
        ecdsa = ec.ECDSA(self.ecdsa_curve.hash_object())
        sig = self.signing_key.sign(data, ecdsa)
        r, s = decode_dss_signature(sig)

        m = Message()
        m.add_string(self.ecdsa_curve.key_format_identifier)
        m.add_string(self._sigencode(r, s))
        return m
Exemple #21
0
    def sign_ssh_data(self, data):
        signer = self.signing_key.signer(ec.ECDSA(hashes.SHA256()))
        signer.update(data)
        sig = signer.finalize()
        r, s = decode_rfc6979_signature(sig)

        m = Message()
        m.add_string('ecdsa-sha2-nistp256')
        m.add_string(self._sigencode(r, s))
        return m
Exemple #22
0
 def _send_eof(self):
     # you are holding the lock.
     if self.eof_sent:
         return None
     m = Message()
     m.add_byte(cMSG_CHANNEL_EOF)
     m.add_int(self.remote_chanid)
     self.eof_sent = True
     self._log(DEBUG, 'EOF sent (%s)', self._name)
     return m
Exemple #23
0
 def test_4_misc(self):
     msg = Message(self.__d)
     self.assertEquals(msg.get_int(), 5)
     self.assertEquals(msg.get_mpint(), 0x1122334455)
     self.assertEquals(msg.get_so_far(), self.__d[:13])
     self.assertEquals(msg.get_remainder(), self.__d[13:])
     msg.rewind()
     self.assertEquals(msg.get_int(), 5)
     self.assertEquals(msg.get_so_far(), self.__d[:4])
     self.assertEquals(msg.get_remainder(), self.__d[4:])
Exemple #24
0
 def invoke_shell(self):
     """
     Request an interactive shell session on this channel.  If the server
     allows it, the channel will then be directly connected to the stdin,
     stdout, and stderr of the shell.
     
     Normally you would call L{get_pty} before this, in which case the
     shell will operate through the pty, and the channel will be connected
     to the stdin and stdout of the pty.
     
     When the shell exits, the channel will be closed and can't be reused.
     You must open a new channel if you wish to open another shell.
     
     @raise SSHException: if the request was rejected or the channel was
         closed
     """
     if self.closed or self.eof_received or self.eof_sent or not self.active:
         raise SSHException('Channel is not open')
     m = Message()
     m.add_byte(chr(MSG_CHANNEL_REQUEST))
     m.add_int(self.remote_chanid)
     m.add_string('shell')
     m.add_boolean(1)
     self.event.clear()
     self.transport._send_user_message(m)
     self._wait_for_event()
Exemple #25
0
    def invoke_shell(self):
        """
        Request an interactive shell session on this channel.  If the server
        allows it, the channel will then be directly connected to the stdin,
        stdout, and stderr of the shell.

        Normally you would call `get_pty` before this, in which case the
        shell will operate through the pty, and the channel will be connected
        to the stdin and stdout of the pty.

        When the shell exits, the channel will be closed and can't be reused.
        You must open a new channel if you wish to open another shell.

        :raises:
            `.SSHException` -- if the request was rejected or the channel was
            closed
        """
        m = Message()
        m.add_byte(cMSG_CHANNEL_REQUEST)
        m.add_int(self.remote_chanid)
        m.add_string("shell")
        m.add_boolean(True)
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #26
0
 def send_stderr(self, s):
     """
     Send data to the channel on the "stderr" stream.  This is normally
     only used by servers to send output from shell commands -- clients
     won't use this.  Returns the number of bytes sent, or 0 if the channel
     stream is closed.  Applications are responsible for checking that all
     data has been sent: if only some of the data was transmitted, the
     application needs to attempt delivery of the remaining data.
     
     :param str s: data to send.
     :return: number of bytes actually sent, as an `int`.
     
     :raises socket.timeout:
         if no data could be sent before the timeout set by `settimeout`.
     
     .. versionadded:: 1.1
     """
     size = len(s)
     self.lock.acquire()
     try:
         size = self._wait_for_send_window(size)
         if size == 0:
             # eof or similar
             return 0
         m = Message()
         m.add_byte(chr(MSG_CHANNEL_EXTENDED_DATA))
         m.add_int(self.remote_chanid)
         m.add_int(1)
         m.add_string(s[:size])
     finally:
         self.lock.release()
     # Note: We release self.lock before calling _send_user_message.
     # Otherwise, we can deadlock during re-keying.
     self.transport._send_user_message(m)
     return size
Exemple #27
0
 def _async_request(self, fileobj, t, *arg):
     # this method may be called from other threads (prefetch)
     self._lock.acquire()
     try:
         msg = Message()
         msg.add_int(self.request_number)
         for item in arg:
             if isinstance(item, long):
                 msg.add_int64(item)
             elif isinstance(item, int):
                 msg.add_int(item)
             elif isinstance(item, (string_types, bytes_types)):
                 msg.add_string(item)
             elif isinstance(item, SFTPAttributes):
                 item._pack(msg)
             else:
                 raise Exception(
                     'unknown type for %r type %r' % (item, type(item)))
         num = self.request_number
         self._expecting[num] = fileobj
         self.request_number += 1
     finally:
         self._lock.release()
     self._send_packet(t, msg)
     return num
Exemple #28
0
 def send_stderr(self, s):
     """
     Send data to the channel on the "stderr" stream.  This is normally
     only used by servers to send output from shell commands -- clients
     won't use this.  Returns the number of bytes sent, or 0 if the channel
     stream is closed.  Applications are responsible for checking that all
     data has been sent: if only some of the data was transmitted, the
     application needs to attempt delivery of the remaining data.
     
     @param s: data to send.
     @type s: str
     @return: number of bytes actually sent.
     @rtype: int
     
     @raise socket.timeout: if no data could be sent before the timeout set
         by L{settimeout}.
     
     @since: 1.1
     """
     size = len(s)
     self.lock.acquire()
     try:
         size = self._wait_for_send_window(size)
         if size == 0:
             # eof or similar
             return 0
         m = Message()
         m.add_byte(chr(MSG_CHANNEL_EXTENDED_DATA))
         m.add_int(self.remote_chanid)
         m.add_int(1)
         m.add_string(s[:size])
         self.transport._send_user_message(m)
     finally:
         self.lock.release()
     return size
Exemple #29
0
 def send_ext_data(self, data):
     m = Message()
     m.add_byte(byte_chr(SSH2_MSG_CHANNEL_EXTENDED_DATA))
     m.add_int(self.channel.remote_chanid)
     m.add_int(SSH2_EXTENDED_DATA_STDERR)
     m.add_string(data)
     self.channel.transport._send_user_message(m)
Exemple #30
0
 def _parse_service_request(self, m):
     service = m.get_string()
     if self.transport.server_mode and (service == 'ssh-userauth'):
         # accepted
         m = Message()
         m.add_byte(chr(MSG_SERVICE_ACCEPT))
         m.add_string(service)
         self.transport._send_message(m)
         return
     # dunno this one
     self._disconnect_service_not_available()
Exemple #31
0
 def _send_message(self, msg):
     msg = str(msg)
     self.conn.send(struct.pack('>I', len(msg)) + msg)
     l = self._read_all(4)
     msg = Message(self._read_all(struct.unpack('>I', l)[0]))
     return ord(msg.get_byte()), msg
Exemple #32
0
    def set_environment_variable(self, name, value):
        """
        Set the value of an environment variable.

        .. warning::
            The server may reject this request depending on its ``AcceptEnv``
            setting; such rejections will fail silently (which is common client
            practice for this particular request type). Make sure you
            understand your server's configuration before using!

        :param str name: name of the environment variable
        :param str value: value of the environment variable

        :raises:
            `.SSHException` -- if the request was rejected or the channel was
            closed
        """
        m = Message()
        m.add_byte(cMSG_CHANNEL_REQUEST)
        m.add_int(self.remote_chanid)
        m.add_string('env')
        m.add_boolean(False)
        m.add_string(name)
        m.add_string(value)
        self.transport._send_user_message(m)
Exemple #33
0
    def invoke_subsystem(self, subsystem):
        """
        Request a subsystem on the server (for example, ``sftp``).  If the
        server allows it, the channel will then be directly connected to the
        requested subsystem.

        When the subsystem finishes, the channel will be closed and can't be
        reused.

        :param str subsystem: name of the subsystem being requested.

        :raises:
            `.SSHException` -- if the request was rejected or the channel was
            closed
        """
        m = Message()
        m.add_byte(cMSG_CHANNEL_REQUEST)
        m.add_int(self.remote_chanid)
        m.add_string('subsystem')
        m.add_boolean(True)
        m.add_string(subsystem)
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #34
0
        @rtype: str
        
        @raise socket.timeout: if no data is ready before the timeout set by
            L{settimeout}.
        
        @since: 1.1
        """
        try:
            out = self.in_stderr_buffer.read(nbytes, self.timeout)
        except PipeTimeout, e:
            raise socket.timeout()

        ack = self._check_add_window(len(out))
        # no need to hold the channel lock when sending this
        if ack > 0:
            m = Message()
            m.add_byte(chr(MSG_CHANNEL_WINDOW_ADJUST))
            m.add_int(self.remote_chanid)
            m.add_int(ack)
            self.transport._send_user_message(m)

        return out

    def send_ready(self):
        """
        Returns true if data can be written to this channel without blocking.
        This means the channel is either closed (so any write attempt would
        return immediately) or there is at least one byte of space in the 
        outbound buffer. If there is at least one byte of space in the
        outbound buffer, a L{send} call will succeed immediately and return
        the number of bytes actually written.
Exemple #35
0
 def __init__(self, agent, blob):
     self.agent = agent
     self.blob = blob
     self.public_blob = None
     self.name = Message(blob).get_text()
Exemple #36
0
    def resize_pty(self, width=80, height=24):
        """
        Resize the pseudo-terminal.  This can be used to change the width and
        height of the terminal emulation created in a previous L{get_pty} call.

        @param width: new width (in characters) of the terminal screen
        @type width: int
        @param height: new height (in characters) of the terminal screen
        @type height: int

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('window-change')
        m.add_boolean(True)
        m.add_int(width)
        m.add_int(height)
        m.add_int(0).add_int(0)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #37
0
    def exec_command(self, command):
        """
        Execute a command on the server.  If the server allows it, the channel
        will then be directly connected to the stdin, stdout, and stderr of
        the command being executed.
        
        When the command finishes executing, the channel will be closed and
        can't be reused.  You must open a new channel if you wish to execute
        another command.

        @param command: a shell command to execute.
        @type command: str

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('exec')
        m.add_boolean(True)
        m.add_string(command)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #38
0
    def _parse_userauth_request(self, m):
        if not self.transport.server_mode:
            # er, uh... what?
            m = Message()
            m.add_byte(cMSG_USERAUTH_FAILURE)
            m.add_string('none')
            m.add_boolean(False)
            self.transport._send_message(m)
            return
        if self.authenticated:
            # ignore
            return
        username = m.get_text()
        service = m.get_text()
        method = m.get_text()
        self.transport._log(
            DEBUG, 'Auth request (type=%s) service=%s, username=%s' %
            (method, service, username))
        if service != 'ssh-connection':
            self._disconnect_service_not_available()
            return
        if (self.auth_username
                is not None) and (self.auth_username != username):
            self.transport._log(
                WARNING,
                'Auth rejected because the client attempted to change username in mid-flight'
            )
            self._disconnect_no_more_auth()
            return
        self.auth_username = username
        # check if GSS-API authentication is enabled
        gss_auth = self.transport.server_object.enable_auth_gssapi()

        if method == 'none':
            result = self.transport.server_object.check_auth_none(username)
        elif method == 'password':
            changereq = m.get_boolean()
            password = m.get_binary()
            try:
                password = password.decode('UTF-8')
            except UnicodeError:
                # some clients/servers expect non-utf-8 passwords!
                # in this case, just return the raw byte string.
                pass
            if changereq:
                # always treated as failure, since we don't support changing passwords, but collect
                # the list of valid auth types from the callback anyway
                self.transport._log(
                    DEBUG, 'Auth request to change passwords (rejected)')
                newpassword = m.get_binary()
                try:
                    newpassword = newpassword.decode('UTF-8', 'replace')
                except UnicodeError:
                    pass
                result = AUTH_FAILED
            else:
                result = self.transport.server_object.check_auth_password(
                    username, password)
        elif method == 'publickey':
            sig_attached = m.get_boolean()
            keytype = m.get_text()
            keyblob = m.get_binary()
            try:
                key = self.transport._key_info[keytype](Message(keyblob))
            except SSHException as e:
                self.transport._log(INFO,
                                    'Auth rejected: public key: %s' % str(e))
                key = None
            except:
                self.transport._log(
                    INFO, 'Auth rejected: unsupported or mangled public key')
                key = None
            if key is None:
                self._disconnect_no_more_auth()
                return
            # first check if this key is okay... if not, we can skip the verify
            result = self.transport.server_object.check_auth_publickey(
                username, key)
            if result != AUTH_FAILED:
                # key is okay, verify it
                if not sig_attached:
                    # client wants to know if this key is acceptable, before it
                    # signs anything...  send special "ok" message
                    m = Message()
                    m.add_byte(cMSG_USERAUTH_PK_OK)
                    m.add_string(keytype)
                    m.add_string(keyblob)
                    self.transport._send_message(m)
                    return
                sig = Message(m.get_binary())
                blob = self._get_session_blob(key, service, username)
                if not key.verify_ssh_sig(blob, sig):
                    self.transport._log(INFO,
                                        'Auth rejected: invalid signature')
                    result = AUTH_FAILED
        elif method == 'keyboard-interactive':
            lang = m.get_string()
            submethods = m.get_string()
            result = self.transport.server_object.check_auth_interactive(
                username, submethods)
            if isinstance(result, InteractiveQuery):
                # make interactive query instead of response
                self._interactive_query(result)
                return
        elif method == "gssapi-with-mic" and gss_auth:
            sshgss = GSSAuth(method)
            # Read the number of OID mechanisms supported by the client.
            # OpenSSH sends just one OID. It's the Kerveros V5 OID and that's
            # the only OID we support.
            mechs = m.get_int()
            # We can't accept more than one OID, so if the SSH client sends
            # more than one, disconnect.
            if mechs > 1:
                self.transport._log(
                    INFO,
                    'Disconnect: Received more than one GSS-API OID mechanism')
                self._disconnect_no_more_auth()
            desired_mech = m.get_string()
            mech_ok = sshgss.ssh_check_mech(desired_mech)
            # if we don't support the mechanism, disconnect.
            if not mech_ok:
                self.transport._log(
                    INFO,
                    'Disconnect: Received an invalid GSS-API OID mechanism')
                self._disconnect_no_more_auth()
            # send the Kerberos V5 GSSAPI OID to the client
            supported_mech = sshgss.ssh_gss_oids("server")
            # RFC 4462 says we are not required to implement GSS-API error
            # messages. See section 3.8 in http://www.ietf.org/rfc/rfc4462.txt
            while True:
                m = Message()
                m.add_byte(cMSG_USERAUTH_GSSAPI_RESPONSE)
                m.add_bytes(supported_mech)
                self.transport._send_message(m)
                ptype, m = self.transport.packetizer.read_message()
                if ptype == MSG_USERAUTH_GSSAPI_TOKEN:
                    client_token = m.get_string()
                    # use the client token as input to establish a secure
                    # context.
                    try:
                        token = sshgss.ssh_accept_sec_context(
                            self.gss_host, client_token, username)
                    except Exception:
                        result = AUTH_FAILED
                        self._send_auth_result(username, method, result)
                        raise
                    if token is not None:
                        m = Message()
                        m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
                        m.add_string(token)
                        self.transport._send_message(m)
                else:
                    raise SSHException("Client asked to handle paket %s" %
                                       MSG_NAMES[ptype])
                # check MIC
                ptype, m = self.transport.packetizer.read_message()
                if ptype == MSG_USERAUTH_GSSAPI_MIC:
                    break
            mic_token = m.get_string()
            try:
                sshgss.ssh_check_mic(mic_token, self.transport.session_id,
                                     username)
            except Exception:
                result = AUTH_FAILED
                self._send_auth_result(username, method, result)
                raise
            if retval == 0:
                # TODO: Implement client credential saving.
                # The OpenSSH server is able to create a TGT with the delegated
                # client credentials, but this is not supported by GSS-API.
                result = AUTH_SUCCESSFUL
                self.transport.server_object.check_auth_gssapi_with_mic(
                    username, result)
            else:
                result = AUTH_FAILED
        elif method == "gssapi-keyex" and gss_auth:
            mic_token = m.get_string()
            sshgss = self.transport.kexgss_ctxt
            if sshgss is None:
                # If there is no valid context, we reject the authentication
                result = AUTH_FAILED
                self._send_auth_result(username, method, result)
            try:
                sshgss.ssh_check_mic(mic_token, self.transport.session_id,
                                     self.auth_username)
            except Exception:
                result = AUTH_FAILED
                self._send_auth_result(username, method, result)
                raise
            if retval == 0:
                result = AUTH_SUCCESSFUL
                self.transport.server_object.check_auth_gssapi_keyex(
                    username, result)
            else:
                result = AUTH_FAILED
        else:
            result = self.transport.server_object.check_auth_none(username)
        # okay, send result
        self._send_auth_result(username, method, result)
Exemple #39
0
 def _interactive_query(self, q):
     # make interactive query instead of response
     m = Message()
     m.add_byte(cMSG_USERAUTH_INFO_REQUEST)
     m.add_string(q.name)
     m.add_string(q.instructions)
     m.add_string(bytes())
     m.add_int(len(q.prompts))
     for p in q.prompts:
         m.add_string(p[0])
         m.add_boolean(p[1])
     self.transport._send_message(m)
Exemple #40
0
 def _parse_kexecdh_init(self, m):
     Q_C_bytes = m.get_string()
     self.Q_C = ec.EllipticCurvePublicNumbers.from_encoded_point(
         self.curve, Q_C_bytes)
     K_S = self.transport.get_server_key().asbytes()
     K = self.P.exchange(ec.ECDH(), self.Q_C.public_key(default_backend()))
     K = long(hexlify(K), 16)
     # compute exchange hash
     hm = Message()
     hm.add(self.transport.remote_version, self.transport.local_version,
            self.transport.remote_kex_init, self.transport.local_kex_init)
     hm.add_string(K_S)
     hm.add_string(Q_C_bytes)
     # SEC1: V2.0  2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
     hm.add_string(self.Q_S.public_numbers().encode_point())
     hm.add_mpint(long(K))
     H = self.hash_algo(hm.asbytes()).digest()
     self.transport._set_K_H(K, H)
     sig = self.transport.get_server_key().sign_ssh_data(H)
     # construct reply
     m = Message()
     m.add_byte(c_MSG_KEXECDH_REPLY)
     m.add_string(K_S)
     m.add_string(self.Q_S.public_numbers().encode_point())
     m.add_string(sig)
     self.transport._send_message(m)
     self.transport._activate_outbound()
Exemple #41
0
    def read_message(self):
        """
        Only one thread should ever be in this function (no other locking is
        done).

        :raises SSHException: if the packet is mangled
        :raises NeedRekeyException: if the transport should rekey
        """
        header = self.read_all(self.__block_size_in, check_rekey=True)
        if self.__block_engine_in is not None:
            header = self.__block_engine_in.decrypt(header)
        if self.__dump_packets:
            self._log(DEBUG, util.format_binary(header, 'IN: '))
        packet_size = struct.unpack('>I', header[:4])[0]
        # leftover contains decrypted bytes from the first block (after the length field)
        leftover = header[4:]
        if (packet_size - len(leftover)) % self.__block_size_in != 0:
            raise SSHException('Invalid packet blocking')
        buf = self.read_all(packet_size + self.__mac_size_in - len(leftover))
        packet = buf[:packet_size - len(leftover)]
        post_packet = buf[packet_size - len(leftover):]
        if self.__block_engine_in is not None:
            packet = self.__block_engine_in.decrypt(packet)
        if self.__dump_packets:
            self._log(DEBUG, util.format_binary(packet, 'IN: '))
        packet = leftover + packet

        if self.__mac_size_in > 0:
            mac = post_packet[:self.__mac_size_in]
            mac_payload = struct.pack('>II', self.__sequence_number_in, packet_size) + packet
            my_mac = compute_hmac(self.__mac_key_in, mac_payload, self.__mac_engine_in)[:self.__mac_size_in]
            if not util.constant_time_bytes_eq(my_mac, mac):
                raise SSHException('Mismatched MAC')
        padding = byte_ord(packet[0])
        payload = packet[1:packet_size - padding]
        
        if self.__dump_packets:
            self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))

        if self.__compress_engine_in is not None:
            payload = self.__compress_engine_in(payload)

        msg = Message(payload[1:])
        msg.seqno = self.__sequence_number_in
        self.__sequence_number_in = (self.__sequence_number_in + 1) & xffffffff

        # check for rekey
        raw_packet_size = packet_size + self.__mac_size_in + 4
        self.__received_bytes += raw_packet_size
        self.__received_packets += 1
        if self.__need_rekey:
            # we've asked to rekey -- give them some packets to comply before
            # dropping the connection
            self.__received_bytes_overflow += raw_packet_size
            self.__received_packets_overflow += 1
            if (self.__received_packets_overflow >= self.REKEY_PACKETS_OVERFLOW_MAX) or \
               (self.__received_bytes_overflow >= self.REKEY_BYTES_OVERFLOW_MAX):
                raise SSHException('Remote transport is ignoring rekey requests')
        elif (self.__received_packets >= self.REKEY_PACKETS) or \
             (self.__received_bytes >= self.REKEY_BYTES):
            # only ask once for rekeying
            self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes received)' %
                      (self.__received_packets, self.__received_bytes))
            self.__received_bytes_overflow = 0
            self.__received_packets_overflow = 0
            self._trigger_rekey()

        cmd = byte_ord(payload[0])
        if cmd in MSG_NAMES:
            cmd_name = MSG_NAMES[cmd]
        else:
            cmd_name = '$%x' % cmd
        if self.__dump_packets:
            self._log(DEBUG, 'Read packet <%s>, length %d' % (cmd_name, len(payload)))
        return cmd, msg
Exemple #42
0
 def _parse_kexecdh_reply(self, m):
     K_S = m.get_string()
     Q_S_bytes = m.get_string()
     self.Q_S = ec.EllipticCurvePublicNumbers.from_encoded_point(
         self.curve, Q_S_bytes)
     sig = m.get_binary()
     K = self.P.exchange(ec.ECDH(), self.Q_S.public_key(default_backend()))
     K = long(hexlify(K), 16)
     # compute exchange hash and verify signature
     hm = Message()
     hm.add(self.transport.local_version, self.transport.remote_version,
            self.transport.local_kex_init, self.transport.remote_kex_init)
     hm.add_string(K_S)
     # SEC1: V2.0  2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
     hm.add_string(self.Q_C.public_numbers().encode_point())
     hm.add_string(Q_S_bytes)
     hm.add_mpint(K)
     self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
     self.transport._verify_key(K_S, sig)
     self.transport._activate_outbound()
Exemple #43
0
 def _sigencode(self, r, s):
     msg = Message()
     msg.add_mpint(r)
     msg.add_mpint(s)
     return msg.asbytes()
Exemple #44
0
 def _sigdecode(self, sig):
     msg = Message(sig)
     r = msg.get_mpint()
     s = msg.get_mpint()
     return r, s
Exemple #45
0
    def request_x11(self,
                    screen_number=0,
                    auth_protocol=None,
                    auth_cookie=None,
                    single_connection=False,
                    handler=None):
        """
        Request an x11 session on this channel.  If the server allows it,
        further x11 requests can be made from the server to the client,
        when an x11 application is run in a shell session.
        
        From RFC4254::

            It is RECOMMENDED that the 'x11 authentication cookie' that is
            sent be a fake, random cookie, and that the cookie be checked and
            replaced by the real cookie when a connection request is received.
        
        If you omit the auth_cookie, a new secure random 128-bit value will be
        generated, used, and returned.  You will need to use this value to
        verify incoming x11 requests and replace them with the actual local
        x11 cookie (which requires some knoweldge of the x11 protocol).
        
        If a handler is passed in, the handler is called from another thread
        whenever a new x11 connection arrives.  The default handler queues up
        incoming x11 connections, which may be retrieved using
        L{Transport.accept}.  The handler's calling signature is::
        
            handler(channel: Channel, (address: str, port: int))
        
        @param screen_number: the x11 screen number (0, 10, etc)
        @type screen_number: int
        @param auth_protocol: the name of the X11 authentication method used;
            if none is given, C{"MIT-MAGIC-COOKIE-1"} is used
        @type auth_protocol: str
        @param auth_cookie: hexadecimal string containing the x11 auth cookie;
            if none is given, a secure random 128-bit value is generated
        @type auth_cookie: str
        @param single_connection: if True, only a single x11 connection will be
            forwarded (by default, any number of x11 connections can arrive
            over this session)
        @type single_connection: bool
        @param handler: an optional handler to use for incoming X11 connections
        @type handler: function
        @return: the auth_cookie used
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        if auth_protocol is None:
            auth_protocol = 'MIT-MAGIC-COOKIE-1'
        if auth_cookie is None:
            auth_cookie = binascii.hexlify(
                self.transport.randpool.get_bytes(16))

        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('x11-req')
        m.add_boolean(True)
        m.add_boolean(single_connection)
        m.add_string(auth_protocol)
        m.add_string(auth_cookie)
        m.add_int(screen_number)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()
        self.transport._set_x11_handler(handler)
        return auth_cookie
Exemple #46
0
 def _send_auth_result(self, username, method, result):
     # okay, send result
     m = Message()
     if result == AUTH_SUCCESSFUL:
         self.transport._log(INFO, 'Auth granted (%s).' % method)
         m.add_byte(cMSG_USERAUTH_SUCCESS)
         self.authenticated = True
     else:
         self.transport._log(INFO, 'Auth rejected (%s).' % method)
         m.add_byte(cMSG_USERAUTH_FAILURE)
         m.add_string(
             self.transport.server_object.get_allowed_auths(username))
         if result == AUTH_PARTIALLY_SUCCESSFUL:
             m.add_boolean(True)
         else:
             m.add_boolean(False)
             self.auth_fail_count += 1
     self.transport._send_message(m)
     if self.auth_fail_count >= 10:
         self._disconnect_no_more_auth()
     if result == AUTH_SUCCESSFUL:
         self.transport._auth_trigger()
Exemple #47
0
 def send_exit_status(self, status):
     """
     Send the exit status of an executed command to the client.  (This
     really only makes sense in server mode.)  Many clients expect to
     get some sort of status code back from an executed command after
     it completes.
     
     @param status: the exit code of the process
     @type status: int
     
     @since: 1.2
     """
     # in many cases, the channel will not still be open here.
     # that's fine.
     m = Message()
     m.add_byte(chr(MSG_CHANNEL_REQUEST))
     m.add_int(self.remote_chanid)
     m.add_string('exit-status')
     m.add_boolean(False)
     m.add_int(status)
     self.transport._send_user_message(m)
Exemple #48
0
 def _parse_service_accept(self, m):
     service = m.get_text()
     if service == 'ssh-userauth':
         self.transport._log(DEBUG, 'userauth is OK')
         m = Message()
         m.add_byte(cMSG_USERAUTH_REQUEST)
         m.add_string(self.username)
         m.add_string('ssh-connection')
         m.add_string(self.auth_method)
         if self.auth_method == 'password':
             m.add_boolean(False)
             password = bytestring(self.password)
             m.add_string(password)
         elif self.auth_method == 'publickey':
             m.add_boolean(True)
             m.add_string(self.private_key.get_name())
             m.add_string(self.private_key)
             blob = self._get_session_blob(self.private_key,
                                           'ssh-connection', self.username)
             sig = self.private_key.sign_ssh_data(blob)
             m.add_string(sig)
         elif self.auth_method == 'keyboard-interactive':
             m.add_string('')
             m.add_string(self.submethods)
         elif self.auth_method == "gssapi-with-mic":
             sshgss = GSSAuth(self.auth_method, self.gss_deleg_creds)
             m.add_bytes(sshgss.ssh_gss_oids())
             # send the supported GSSAPI OIDs to the server
             self.transport._send_message(m)
             ptype, m = self.transport.packetizer.read_message()
             if ptype == MSG_USERAUTH_BANNER:
                 self._parse_userauth_banner(m)
                 ptype, m = self.transport.packetizer.read_message()
             if ptype == MSG_USERAUTH_GSSAPI_RESPONSE:
                 # Read the mechanism selected by the server. We send just
                 # the Kerberos V5 OID, so the server can only respond with
                 # this OID.
                 mech = m.get_string()
                 m = Message()
                 m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
                 m.add_string(
                     sshgss.ssh_init_sec_context(
                         self.gss_host,
                         mech,
                         self.username,
                     ))
                 self.transport._send_message(m)
                 while True:
                     ptype, m = self.transport.packetizer.read_message()
                     if ptype == MSG_USERAUTH_GSSAPI_TOKEN:
                         srv_token = m.get_string()
                         next_token = sshgss.ssh_init_sec_context(
                             self.gss_host, mech, self.username, srv_token)
                         # After this step the GSSAPI should not return any
                         # token. If it does, we keep sending the token to
                         # the server until no more token is returned.
                         if next_token is None:
                             break
                         else:
                             m = Message()
                             m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN)
                             m.add_string(next_token)
                             self.transport.send_message(m)
                 else:
                     raise SSHException("Received Package: %s" %
                                        MSG_NAMES[ptype])
                 m = Message()
                 m.add_byte(cMSG_USERAUTH_GSSAPI_MIC)
                 # send the MIC to the server
                 m.add_string(sshgss.ssh_get_mic(self.transport.session_id))
             elif ptype == MSG_USERAUTH_GSSAPI_ERRTOK:
                 # RFC 4462 says we are not required to implement GSS-API
                 # error messages.
                 # See RFC 4462 Section 3.8 in
                 # http://www.ietf.org/rfc/rfc4462.txt
                 raise SSHException("Server returned an error token")
             elif ptype == MSG_USERAUTH_GSSAPI_ERROR:
                 maj_status = m.get_int()
                 min_status = m.get_int()
                 err_msg = m.get_string()
                 lang_tag = m.get_string()  # we don't care!
                 raise SSHException("GSS-API Error:\nMajor Status: %s\n\
                                     Minor Status: %s\ \nError Message:\
                                      %s\n") % (str(maj_status),
                                                str(min_status), err_msg)
             elif ptype == MSG_USERAUTH_FAILURE:
                 self._parse_userauth_failure(m)
                 return
             else:
                 raise SSHException("Received Package: %s" %
                                    MSG_NAMES[ptype])
         elif self.auth_method == 'gssapi-keyex' and\
             self.transport.gss_kex_used:
             kexgss = self.transport.kexgss_ctxt
             kexgss.set_username(self.username)
             mic_token = kexgss.ssh_get_mic(self.transport.session_id)
             m.add_string(mic_token)
         elif self.auth_method == 'none':
             pass
         else:
             raise SSHException('Unknown auth method "%s"' %
                                self.auth_method)
         self.transport._send_message(m)
     else:
         self.transport._log(DEBUG,
                             'Service request "%s" accepted (?)' % service)
Exemple #49
0
    def invoke_subsystem(self, subsystem):
        """
        Request a subsystem on the server (for example, C{sftp}).  If the
        server allows it, the channel will then be directly connected to the
        requested subsystem.
        
        When the subsystem finishes, the channel will be closed and can't be
        reused.

        @param subsystem: name of the subsystem being requested.
        @type subsystem: str

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('subsystem')
        m.add_boolean(True)
        m.add_string(subsystem)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #50
0
 def _get_session_blob(self, key, service, username):
     m = Message()
     m.add_string(self.transport.session_id)
     m.add_byte(cMSG_USERAUTH_REQUEST)
     m.add_string(username)
     m.add_string(service)
     m.add_string('publickey')
     m.add_boolean(True)
     m.add_string(key.get_name())
     m.add_string(key)
     return m.asbytes()
Exemple #51
0
    def get_pty(self, term='vt100', width=80, height=24):
        """
        Request a pseudo-terminal from the server.  This is usually used right
        after creating a client channel, to ask the server to provide some
        basic terminal semantics for a shell invoked with L{invoke_shell}.
        It isn't necessary (or desirable) to call this method if you're going
        to exectue a single command with L{exec_command}.

        @param term: the terminal type to emulate (for example, C{'vt100'})
        @type term: str
        @param width: width (in characters) of the terminal screen
        @type width: int
        @param height: height (in characters) of the terminal screen
        @type height: int
        
        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('pty-req')
        m.add_boolean(True)
        m.add_string(term)
        m.add_int(width)
        m.add_int(height)
        # pixel height, width (usually useless)
        m.add_int(0).add_int(0)
        m.add_string('')
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #52
0
 def _request_auth(self):
     m = Message()
     m.add_byte(cMSG_SERVICE_REQUEST)
     m.add_string('ssh-userauth')
     self.transport._send_message(m)
Exemple #53
0
 def add_key(self, key):
     header = asbytes(cSSH_AGENTC_ADD_IDENTITY)
     body = Message()
     body.add_string("ssh-rsa")
     body.add_mpint(key.public_numbers.n)
     body.add_mpint(key.public_numbers.e)
     body.add_mpint(key.d)
     body.add_mpint(key.iqmp)
     body.add_mpint(key.p)
     body.add_mpint(key.q)
     body.add_string("")
     packet_len = len(header) + body.packet.tell()
     self._conn.send(
         struct.pack(">I", packet_len) + header + body.packet.getvalue())
     data = self._read_all(4)
     msg = Message(self._read_all(struct.unpack(">I", data)[0]))
     if ord(msg.get_byte()) != SSH_AGENT_SUCCESS:
         raise SSHException("could not add key to agent")
     return
Exemple #54
0
    def listdir_iter(self, path='.', read_aheads=50):
        """
        Generator version of `.listdir_attr`.

        See the API docs for `.listdir_attr` for overall details.

        This function adds one more kwarg on top of `.listdir_attr`:
        ``read_aheads``, an integer controlling how many
        ``SSH_FXP_READDIR`` requests are made to the server. The default of 50
        should suffice for most file listings as each request/response cycle
        may contain multiple files (dependant on server implementation.)

        .. versionadded:: 1.15
        """
        path = self._adjust_cwd(path)
        self._log(DEBUG, 'listdir(%r)' % path)
        t, msg = self._request(CMD_OPENDIR, path)

        if t != CMD_HANDLE:
            raise SFTPError('Expected handle')

        handle = msg.get_string()

        nums = list()
        while True:
            try:
                # Send out a bunch of readdir requests so that we can read the
                # responses later on Section 6.7 of the SSH file transfer RFC
                # explains this
                # http://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
                for i in range(read_aheads):
                    num = self._async_request(type(None), CMD_READDIR, handle)
                    nums.append(num)

                # For each of our sent requests
                # Read and parse the corresponding packets
                # If we're at the end of our queued requests, then fire off
                # some more requests
                # Exit the loop when we've reached the end of the directory
                # handle
                for num in nums:
                    t, pkt_data = self._read_packet()
                    msg = Message(pkt_data)
                    new_num = msg.get_int()
                    if num == new_num:
                        if t == CMD_STATUS:
                            self._convert_status(msg)
                    count = msg.get_int()
                    for i in range(count):
                        filename = msg.get_text()
                        longname = msg.get_text()
                        attr = SFTPAttributes._from_msg(
                            msg, filename, longname)
                        if (filename != '.') and (filename != '..'):
                            yield attr

                # If we've hit the end of our queued requests, reset nums.
                nums = list()

            except EOFError:
                self._request(CMD_CLOSE, handle)
                return
Exemple #55
0
 def _send_message(self, msg):
     msg = asbytes(msg)
     self._conn.send(struct.pack(">I", len(msg)) + msg)
     data = self._read_all(4)
     msg = Message(self._read_all(struct.unpack(">I", data)[0]))
     return ord(msg.get_byte()), msg
Exemple #56
0
 def _handle_request(self, m):
     key = m.get_string()
     want_reply = m.get_boolean()
     server = self.transport.server_object
     ok = False
     if key == 'exit-status':
         self.exit_status = m.get_int()
         self.status_event.set()
         ok = True
     elif key == 'xon-xoff':
         # ignore
         ok = True
     elif key == 'pty-req':
         term = m.get_string()
         width = m.get_int()
         height = m.get_int()
         pixelwidth = m.get_int()
         pixelheight = m.get_int()
         modes = m.get_string()
         if server is None:
             ok = False
         else:
             ok = server.check_channel_pty_request(self, term, width,
                                                   height, pixelwidth,
                                                   pixelheight, modes)
     elif key == 'shell':
         if server is None:
             ok = False
         else:
             ok = server.check_channel_shell_request(self)
     elif key == 'exec':
         cmd = m.get_string()
         if server is None:
             ok = False
         else:
             ok = server.check_channel_exec_request(self, cmd)
     elif key == 'subsystem':
         name = m.get_string()
         if server is None:
             ok = False
         else:
             ok = server.check_channel_subsystem_request(self, name)
     elif key == 'window-change':
         width = m.get_int()
         height = m.get_int()
         pixelwidth = m.get_int()
         pixelheight = m.get_int()
         if server is None:
             ok = False
         else:
             ok = server.check_channel_window_change_request(
                 self, width, height, pixelwidth, pixelheight)
     elif key == 'x11-req':
         single_connection = m.get_boolean()
         auth_proto = m.get_string()
         auth_cookie = m.get_string()
         screen_number = m.get_int()
         if server is None:
             ok = False
         else:
             ok = server.check_channel_x11_request(self, single_connection,
                                                   auth_proto, auth_cookie,
                                                   screen_number)
     else:
         self._log(DEBUG, 'Unhandled channel request "%s"' % key)
         ok = False
     if want_reply:
         m = Message()
         if ok:
             m.add_byte(chr(MSG_CHANNEL_SUCCESS))
         else:
             m.add_byte(chr(MSG_CHANNEL_FAILURE))
         m.add_int(self.remote_chanid)
         self.transport._send_user_message(m)
Exemple #57
0
    def resize_pty(self, width=80, height=24, width_pixels=0, height_pixels=0):
        """
        Resize the pseudo-terminal.  This can be used to change the width and
        height of the terminal emulation created in a previous `get_pty` call.

        :param int width: new width (in characters) of the terminal screen
        :param int height: new height (in characters) of the terminal screen
        :param int width_pixels: new width (in pixels) of the terminal screen
        :param int height_pixels: new height (in pixels) of the terminal screen

        :raises:
            `.SSHException` -- if the request was rejected or the channel was
            closed
        """
        m = Message()
        m.add_byte(cMSG_CHANNEL_REQUEST)
        m.add_int(self.remote_chanid)
        m.add_string('window-change')
        m.add_boolean(False)
        m.add_int(width)
        m.add_int(height)
        m.add_int(width_pixels)
        m.add_int(height_pixels)
        self.transport._send_user_message(m)
Exemple #58
0
class Channel(object):
    """
    A secure tunnel across an SSH L{Transport}.  A Channel is meant to behave
    like a socket, and has an API that should be indistinguishable from the
    python socket API.

    Because SSH2 has a windowing kind of flow control, if you stop reading data
    from a Channel and its buffer fills up, the server will be unable to send
    you any more data until you read some of it.  (This won't affect other
    channels on the same transport -- all channels on a single transport are
    flow-controlled independently.)  Similarly, if the server isn't reading
    data you send, calls to L{send} may block, unless you set a timeout.  This
    is exactly like a normal network socket, so it shouldn't be too surprising.
    """
    def __init__(self, chanid):
        """
        Create a new channel.  The channel is not associated with any
        particular session or L{Transport} until the Transport attaches it.
        Normally you would only call this method from the constructor of a
        subclass of L{Channel}.

        @param chanid: the ID of this channel, as passed by an existing
            L{Transport}.
        @type chanid: int
        """
        self.chanid = chanid
        self.remote_chanid = 0
        self.transport = None
        self.active = False
        self.eof_received = 0
        self.eof_sent = 0
        self.in_buffer = BufferedPipe()
        self.in_stderr_buffer = BufferedPipe()
        self.timeout = None
        self.closed = False
        self.ultra_debug = False
        self.lock = threading.Lock()
        self.out_buffer_cv = threading.Condition(self.lock)
        self.in_window_size = 0
        self.out_window_size = 0
        self.in_max_packet_size = 0
        self.out_max_packet_size = 0
        self.in_window_threshold = 0
        self.in_window_sofar = 0
        self.status_event = threading.Event()
        self._name = str(chanid)
        self.logger = util.get_logger('paramiko.transport')
        self._pipe = None
        self.event = threading.Event()
        self.combine_stderr = False
        self.exit_status = -1
        self.origin_addr = None

    def __del__(self):
        try:
            self.close()
        except:
            pass

    def __repr__(self):
        """
        Return a string representation of this object, for debugging.

        @rtype: str
        """
        out = '<paramiko.Channel %d' % self.chanid
        if self.closed:
            out += ' (closed)'
        elif self.active:
            if self.eof_received:
                out += ' (EOF received)'
            if self.eof_sent:
                out += ' (EOF sent)'
            out += ' (open) window=%d' % (self.out_window_size)
            if len(self.in_buffer) > 0:
                out += ' in-buffer=%d' % (len(self.in_buffer), )
        out += ' -> ' + repr(self.transport)
        out += '>'
        return out

    def get_pty(self, term='vt100', width=80, height=24):
        """
        Request a pseudo-terminal from the server.  This is usually used right
        after creating a client channel, to ask the server to provide some
        basic terminal semantics for a shell invoked with L{invoke_shell}.
        It isn't necessary (or desirable) to call this method if you're going
        to exectue a single command with L{exec_command}.

        @param term: the terminal type to emulate (for example, C{'vt100'})
        @type term: str
        @param width: width (in characters) of the terminal screen
        @type width: int
        @param height: height (in characters) of the terminal screen
        @type height: int
        
        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('pty-req')
        m.add_boolean(True)
        m.add_string(term)
        m.add_int(width)
        m.add_int(height)
        # pixel height, width (usually useless)
        m.add_int(0).add_int(0)
        m.add_string('')
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()

    def invoke_shell(self):
        """
        Request an interactive shell session on this channel.  If the server
        allows it, the channel will then be directly connected to the stdin,
        stdout, and stderr of the shell.
        
        Normally you would call L{get_pty} before this, in which case the
        shell will operate through the pty, and the channel will be connected
        to the stdin and stdout of the pty.
        
        When the shell exits, the channel will be closed and can't be reused.
        You must open a new channel if you wish to open another shell.
        
        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('shell')
        m.add_boolean(1)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()

    def exec_command(self, command):
        """
        Execute a command on the server.  If the server allows it, the channel
        will then be directly connected to the stdin, stdout, and stderr of
        the command being executed.
        
        When the command finishes executing, the channel will be closed and
        can't be reused.  You must open a new channel if you wish to execute
        another command.

        @param command: a shell command to execute.
        @type command: str

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('exec')
        m.add_boolean(True)
        m.add_string(command)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()

    def invoke_subsystem(self, subsystem):
        """
        Request a subsystem on the server (for example, C{sftp}).  If the
        server allows it, the channel will then be directly connected to the
        requested subsystem.
        
        When the subsystem finishes, the channel will be closed and can't be
        reused.

        @param subsystem: name of the subsystem being requested.
        @type subsystem: str

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('subsystem')
        m.add_boolean(True)
        m.add_string(subsystem)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()

    def resize_pty(self, width=80, height=24):
        """
        Resize the pseudo-terminal.  This can be used to change the width and
        height of the terminal emulation created in a previous L{get_pty} call.

        @param width: new width (in characters) of the terminal screen
        @type width: int
        @param height: new height (in characters) of the terminal screen
        @type height: int

        @raise SSHException: if the request was rejected or the channel was
            closed
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('window-change')
        m.add_boolean(True)
        m.add_int(width)
        m.add_int(height)
        m.add_int(0).add_int(0)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()

    def exit_status_ready(self):
        """
        Return true if the remote process has exited and returned an exit
        status. You may use this to poll the process status if you don't
        want to block in L{recv_exit_status}. Note that the server may not
        return an exit status in some cases (like bad servers).
        
        @return: True if L{recv_exit_status} will return immediately
        @rtype: bool
        @since: 1.7.3
        """
        return self.closed or self.status_event.isSet()

    def recv_exit_status(self):
        """
        Return the exit status from the process on the server.  This is
        mostly useful for retrieving the reults of an L{exec_command}.
        If the command hasn't finished yet, this method will wait until
        it does, or until the channel is closed.  If no exit status is
        provided by the server, -1 is returned.
        
        @return: the exit code of the process on the server.
        @rtype: int
        
        @since: 1.2
        """
        while True:
            if self.closed or self.status_event.isSet():
                break
            self.status_event.wait(0.1)
        return self.exit_status

    def send_exit_status(self, status):
        """
        Send the exit status of an executed command to the client.  (This
        really only makes sense in server mode.)  Many clients expect to
        get some sort of status code back from an executed command after
        it completes.
        
        @param status: the exit code of the process
        @type status: int
        
        @since: 1.2
        """
        # in many cases, the channel will not still be open here.
        # that's fine.
        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('exit-status')
        m.add_boolean(False)
        m.add_int(status)
        self.transport._send_user_message(m)

    def request_x11(self,
                    screen_number=0,
                    auth_protocol=None,
                    auth_cookie=None,
                    single_connection=False,
                    handler=None):
        """
        Request an x11 session on this channel.  If the server allows it,
        further x11 requests can be made from the server to the client,
        when an x11 application is run in a shell session.
        
        From RFC4254::

            It is RECOMMENDED that the 'x11 authentication cookie' that is
            sent be a fake, random cookie, and that the cookie be checked and
            replaced by the real cookie when a connection request is received.
        
        If you omit the auth_cookie, a new secure random 128-bit value will be
        generated, used, and returned.  You will need to use this value to
        verify incoming x11 requests and replace them with the actual local
        x11 cookie (which requires some knoweldge of the x11 protocol).
        
        If a handler is passed in, the handler is called from another thread
        whenever a new x11 connection arrives.  The default handler queues up
        incoming x11 connections, which may be retrieved using
        L{Transport.accept}.  The handler's calling signature is::
        
            handler(channel: Channel, (address: str, port: int))
        
        @param screen_number: the x11 screen number (0, 10, etc)
        @type screen_number: int
        @param auth_protocol: the name of the X11 authentication method used;
            if none is given, C{"MIT-MAGIC-COOKIE-1"} is used
        @type auth_protocol: str
        @param auth_cookie: hexadecimal string containing the x11 auth cookie;
            if none is given, a secure random 128-bit value is generated
        @type auth_cookie: str
        @param single_connection: if True, only a single x11 connection will be
            forwarded (by default, any number of x11 connections can arrive
            over this session)
        @type single_connection: bool
        @param handler: an optional handler to use for incoming X11 connections
        @type handler: function
        @return: the auth_cookie used
        """
        if self.closed or self.eof_received or self.eof_sent or not self.active:
            raise SSHException('Channel is not open')
        if auth_protocol is None:
            auth_protocol = 'MIT-MAGIC-COOKIE-1'
        if auth_cookie is None:
            auth_cookie = binascii.hexlify(
                self.transport.randpool.get_bytes(16))

        m = Message()
        m.add_byte(chr(MSG_CHANNEL_REQUEST))
        m.add_int(self.remote_chanid)
        m.add_string('x11-req')
        m.add_boolean(True)
        m.add_boolean(single_connection)
        m.add_string(auth_protocol)
        m.add_string(auth_cookie)
        m.add_int(screen_number)
        self.event.clear()
        self.transport._send_user_message(m)
        self._wait_for_event()
        self.transport._set_x11_handler(handler)
        return auth_cookie

    def get_transport(self):
        """
        Return the L{Transport} associated with this channel.

        @return: the L{Transport} that was used to create this channel.
        @rtype: L{Transport}
        """
        return self.transport

    def set_name(self, name):
        """
        Set a name for this channel.  Currently it's only used to set the name
        of the channel in logfile entries.  The name can be fetched with the
        L{get_name} method.

        @param name: new channel name
        @type name: str
        """
        self._name = name

    def get_name(self):
        """
        Get the name of this channel that was previously set by L{set_name}.

        @return: the name of this channel.
        @rtype: str
        """
        return self._name

    def get_id(self):
        """
        Return the ID # for this channel.  The channel ID is unique across
        a L{Transport} and usually a small number.  It's also the number
        passed to L{ServerInterface.check_channel_request} when determining
        whether to accept a channel request in server mode.

        @return: the ID of this channel.
        @rtype: int
        """
        return self.chanid

    def set_combine_stderr(self, combine):
        """
        Set whether stderr should be combined into stdout on this channel.
        The default is C{False}, but in some cases it may be convenient to
        have both streams combined.
        
        If this is C{False}, and L{exec_command} is called (or C{invoke_shell}
        with no pty), output to stderr will not show up through the L{recv}
        and L{recv_ready} calls.  You will have to use L{recv_stderr} and
        L{recv_stderr_ready} to get stderr output.
        
        If this is C{True}, data will never show up via L{recv_stderr} or
        L{recv_stderr_ready}.
        
        @param combine: C{True} if stderr output should be combined into
            stdout on this channel.
        @type combine: bool
        @return: previous setting.
        @rtype: bool
        
        @since: 1.1
        """
        data = ''
        self.lock.acquire()
        try:
            old = self.combine_stderr
            self.combine_stderr = combine
            if combine and not old:
                # copy old stderr buffer into primary buffer
                data = self.in_stderr_buffer.empty()
        finally:
            self.lock.release()
        if len(data) > 0:
            self._feed(data)
        return old

    ###  socket API

    def settimeout(self, timeout):
        """
        Set a timeout on blocking read/write operations.  The C{timeout}
        argument can be a nonnegative float expressing seconds, or C{None}.  If
        a float is given, subsequent channel read/write operations will raise
        a timeout exception if the timeout period value has elapsed before the
        operation has completed.  Setting a timeout of C{None} disables
        timeouts on socket operations.

        C{chan.settimeout(0.0)} is equivalent to C{chan.setblocking(0)};
        C{chan.settimeout(None)} is equivalent to C{chan.setblocking(1)}.

        @param timeout: seconds to wait for a pending read/write operation
            before raising C{socket.timeout}, or C{None} for no timeout.
        @type timeout: float
        """
        self.timeout = timeout

    def gettimeout(self):
        """
        Returns the timeout in seconds (as a float) associated with socket
        operations, or C{None} if no timeout is set.  This reflects the last
        call to L{setblocking} or L{settimeout}.

        @return: timeout in seconds, or C{None}.
        @rtype: float
        """
        return self.timeout

    def setblocking(self, blocking):
        """
        Set blocking or non-blocking mode of the channel: if C{blocking} is 0,
        the channel is set to non-blocking mode; otherwise it's set to blocking
        mode.  Initially all channels are in blocking mode.

        In non-blocking mode, if a L{recv} call doesn't find any data, or if a
        L{send} call can't immediately dispose of the data, an error exception
        is raised.  In blocking mode, the calls block until they can proceed.

        C{chan.setblocking(0)} is equivalent to C{chan.settimeout(0)};
        C{chan.setblocking(1)} is equivalent to C{chan.settimeout(None)}.

        @param blocking: 0 to set non-blocking mode; non-0 to set blocking
        mode.
        @type blocking: int
        """
        if blocking:
            self.settimeout(None)
        else:
            self.settimeout(0.0)

    def getpeername(self):
        """
        Return the address of the remote side of this Channel, if possible.
        This is just a wrapper around C{'getpeername'} on the Transport, used
        to provide enough of a socket-like interface to allow asyncore to work.
        (asyncore likes to call C{'getpeername'}.)

        @return: the address if the remote host, if known
        @rtype: tuple(str, int)
        """
        return self.transport.getpeername()

    def close(self):
        """
        Close the channel.  All future read/write operations on the channel
        will fail.  The remote end will receive no more data (after queued data
        is flushed).  Channels are automatically closed when their L{Transport}
        is closed or when they are garbage collected.
        """
        self.lock.acquire()
        try:
            # only close the pipe when the user explicitly closes the channel.
            # otherwise they will get unpleasant surprises.  (and do it before
            # checking self.closed, since the remote host may have already
            # closed the connection.)
            if self._pipe is not None:
                self._pipe.close()
                self._pipe = None

            if not self.active or self.closed:
                return
            msgs = self._close_internal()
        finally:
            self.lock.release()
        for m in msgs:
            if m is not None:
                self.transport._send_user_message(m)

    def recv_ready(self):
        """
        Returns true if data is buffered and ready to be read from this
        channel.  A C{False} result does not mean that the channel has closed;
        it means you may need to wait before more data arrives.
        
        @return: C{True} if a L{recv} call on this channel would immediately
            return at least one byte; C{False} otherwise.
        @rtype: boolean
        """
        return self.in_buffer.read_ready()

    def recv(self, nbytes):
        """
        Receive data from the channel.  The return value is a string
        representing the data received.  The maximum amount of data to be
        received at once is specified by C{nbytes}.  If a string of length zero
        is returned, the channel stream has closed.

        @param nbytes: maximum number of bytes to read.
        @type nbytes: int
        @return: data.
        @rtype: str
        
        @raise socket.timeout: if no data is ready before the timeout set by
            L{settimeout}.
        """
        try:
            out = self.in_buffer.read(nbytes, self.timeout)
        except PipeTimeout, e:
            raise socket.timeout()

        ack = self._check_add_window(len(out))
        # no need to hold the channel lock when sending this
        if ack > 0:
            m = Message()
            m.add_byte(chr(MSG_CHANNEL_WINDOW_ADJUST))
            m.add_int(self.remote_chanid)
            m.add_int(ack)
            self.transport._send_user_message(m)

        return out
Exemple #59
0
 def __init__(self, agent, blob):
     self.agent = agent
     self.blob = blob
     self.name = Message(blob).get_string()
Exemple #60
0
    def exec_command(self, command, timeout=90):
        """
        Execute a command on the server.  If the server allows it, the channel
        will then be directly connected to the stdin, stdout, and stderr of
        the command being executed.

        When the command finishes executing, the channel will be closed and
        can't be reused.  You must open a new channel if you wish to execute
        another command.

        :param str command: a shell command to execute.

        :param int timeout: set thread timeout when waiting for event

        :raises:
            `.SSHException` -- if the request was rejected or the channel was
            closed
        """
        m = Message()
        m.add_byte(cMSG_CHANNEL_REQUEST)
        m.add_int(self.remote_chanid)
        m.add_string('exec')
        m.add_boolean(True)
        m.add_string(command)
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event(timeout=timeout)