Exemple #1
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
        @return: C{True} if the operation succeeded; C{False} if not.
        @rtype: bool
        """
        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(1)
        m.add_string(command)
        self.event.clear()
        self.transport._send_user_message(m)
        while True:
            self.event.wait(0.1)
            if self.closed:
                return False
            if self.event.isSet():
                return True
Exemple #2
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
        @return: C{True} if the operation succeeded; C{False} if not.
        @rtype: bool
        """
        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(1)
        m.add_string(subsystem)
        self.event.clear()
        self.transport._send_user_message(m)
        while True:
            self.event.wait(0.1)
            if self.closed:
                return False
            if self.event.isSet():
                return True
Exemple #3
0
    def send_datagram(self, s):
        """
        Send all data to the channel in one message.  Returns the number of bytes sent,
        or 0 if the channel stream is closed.

        @param s: data to send
        @type s: str
        @return: number of bytes actually sent (C{len(s)} or C{0})
        @rtype: int

        @raise socket.timeout: if no data could be sent before the timeout set
            by L{settimeout}.
        """
        self.lock.acquire()
        size = 0
        try:
            while size < len(s):
                size = self._wait_for_send_window(len(s), account = False)
            self.out_window_size -= len(s)
            m = Message()
            m.add_byte(chr(MSG_CHANNEL_DATA))
            m.add_int(self.remote_chanid)
            m.add_string(s)
        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 #4
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
 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 #6
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 #7
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 #8
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 #9
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 #10
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
        """
        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(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 #11
0
    def send(self, s):
        """
        Send data to the channel.  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}.
        """
        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_DATA))
            m.add_int(self.remote_chanid)
            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 #12
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 #13
0
def make_certificate(ca_key, duration_hours, real_name, username, host, now, expiry):
    """http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.certkeys?rev=1.9"""
    pkey = paramiko.RSAKey.from_private_key(StringIO(ca_key))

    principals = Message()
    principals.add_string(username)
    principals = principals.asbytes()

    m = Message()
    m.add_string('*****@*****.**')
    m.add_string(sha1(str(random.random())).hexdigest())
    m.add_mpint(pkey.e)
    m.add_mpint(pkey.n)
    m.add_int64(0) # serial
    m.add_int(SSH_CERT_TYPE_USER)
    m.add_string(real_name)
    m.add_string(principals)
    m.add_int64(int(now.strftime("%s")))
    m.add_int64(int(expiry.strftime("%s")))
    m.add_string("") # critical_options
    m.add_string("") # extensions
    m.add_string("") # reserved
    m.add_string(pkey.asbytes())

    key = RSA.construct((long(pkey.n), long(pkey.e), long(pkey.d)))
    h = SHA.new(m.asbytes())
    signer = PKCS1_v1_5.new(key)
    signature = signer.sign(h)

    sig_message = Message()
    sig_message.add_string("ssh-rsa")
    sig_message.add_string(signature)
    m.add_string(sig_message.asbytes())

    return "[email protected] {0} {1}@{2}".format(base64.b64encode(m.asbytes()), username, host)
Exemple #14
0
    def get_pty(self, term='vt100', width=80, height=24, width_pixels=0,
                height_pixels=0):
        """
        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 `invoke_shell`.
        It isn't necessary (or desirable) to call this method if you're going
        to exectue a single command with `exec_command`.

        :param str term: the terminal type to emulate (for example, ``'vt100'``)
        :param int width: width (in characters) of the terminal screen
        :param int height: height (in characters) of the terminal screen
        :param int width_pixels: width (in pixels) of the terminal screen
        :param int height_pixels: 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('pty-req')
        m.add_boolean(True)
        m.add_string(term)
        m.add_int(width)
        m.add_int(height)
        m.add_int(width_pixels)
        m.add_int(height_pixels)
        m.add_string(bytes())
        self._event_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
Exemple #15
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
        `.Transport.accept`.  The handler's calling signature is::
        
            handler(channel: Channel, (address: str, port: int))
        
        :param int screen_number: the x11 screen number (0, 10, etc)
        :param str auth_protocol:
            the name of the X11 authentication method used; if none is given,
            ``"MIT-MAGIC-COOKIE-1"`` is used
        :param str auth_cookie:
            hexadecimal string containing the x11 auth cookie; if none is
            given, a secure random 128-bit value is generated
        :param bool single_connection:
            if True, only a single x11 connection will be forwarded (by
            default, any number of x11 connections can arrive over this
            session)
        :param function handler:
            an optional handler to use for incoming X11 connections
        :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(os.urandom(16))

        m = Message()
        m.add_byte(cMSG_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_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
        self.transport._set_x11_handler(handler)
        return auth_cookie
Exemple #16
0
    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 ``nbytes``.  If a string of length zero
        is returned, the channel stream has closed.

        :param int nbytes: maximum number of bytes to read.
        :return: received data, as a `bytes`

        :raises socket.timeout:
            if no data is ready before the timeout set by `settimeout`.
        """
        try:
            out = self.in_buffer.read(nbytes, self.timeout)
        except PipeTimeout:
            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(cMSG_CHANNEL_WINDOW_ADJUST)
            m.add_int(self.remote_chanid)
            m.add_int(ack)
            self.transport._send_user_message(m)

        return out
Exemple #17
0
    def recv_stderr(self, nbytes):
        """
        Receive data from the channel's stderr stream.  Only channels using
        L{exec_command} or L{invoke_shell} without a pty will ever have data
        on the stderr stream.  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}.

        @since: 1.1
        """
        try:
            out = self.in_stderr_buffer.read(nbytes, self.timeout)
        except PipeTimeout as 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 #18
0
    def recv_stderr(self, nbytes):
        """
        Receive data from the channel's stderr stream.  Only channels using
        `exec_command` or `invoke_shell` without a pty will ever have data
        on the stderr stream.  The return value is a string representing the
        data received.  The maximum amount of data to be received at once is
        specified by ``nbytes``.  If a string of length zero is returned, the
        channel stream has closed.

        :param int nbytes: maximum number of bytes to read.
        :return: received data as a `str`

        :raises socket.timeout: if no data is ready before the timeout set by
            `settimeout`.

        .. versionadded:: 1.1
        """
        try:
            out = self.in_stderr_buffer.read(nbytes, self.timeout)
        except PipeTimeout:
            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(cMSG_CHANNEL_WINDOW_ADJUST)
            m.add_int(self.remote_chanid)
            m.add_int(ack)
            self.transport._send_user_message(m)

        return out
Exemple #19
0
    def test_1_encode(self):
        msg = Message()
        msg.add_int(23)
        msg.add_int(123789456)
        msg.add_string("q")
        msg.add_string("hello")
        msg.add_string("x" * 1000)
        self.assertEqual(msg.asbytes(), self.__a)

        msg = Message()
        msg.add_boolean(True)
        msg.add_boolean(False)
        msg.add_byte(byte_chr(0xf3))

        msg.add_bytes(zero_byte + byte_chr(0x3f))
        msg.add_list(["huey", "dewey", "louie"])
        self.assertEqual(msg.asbytes(), self.__b)

        msg = Message()
        msg.add_int64(5)
        msg.add_int64(0xf5e4d3c2b109)
        msg.add_mpint(17)
        msg.add_mpint(0xf5e4d3c2b109)
        msg.add_mpint(-0x65e4d3c2b109)
        self.assertEqual(msg.asbytes(), self.__c)
Exemple #20
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 str command: a shell command to execute.

        :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()
Exemple #21
0
 def test_handle_13(self):
     # Test handling a SSH2_AGENTC_SIGN_REQUEST
     msg = Message()
     # Please sign some data
     msg.add_byte(byte_chr(13))
     # The id of the key to sign with
     key = list(self.agent.identities.values())[0][0].asbytes()
     msg.add_int(len(key))
     msg.add_bytes(bytes(key))
     # A blob of binary to sign
     blob = b'\x0e' * 10
     msg.add_int(len(blob))
     msg.add_bytes(blob)
     # Go go go
     mtype, msg = self.send(msg)
     self.assertEqual(mtype, 14)
     self.assertEqual(binascii.hexlify(msg.get_binary()), force_bytes((
         '000000077373682d7273610000010031d4c2bfad183557a7055f005c3d0d838d5'
         '701bd7b8a09d6d7f06699c691842c18e2bb62504a4beba0fbf5aeaf62f8106352'
         'b99f60d1fdc2dac1f5ad29566022eff25f62fac38cb2db849ed6b862af5e6bd36'
         '09b249a099848aa6fcfdfe1d93d2538ab4e614ecc95a4282abf8742c7bb591db9'
         '3e049e70a559d29134d207018a650b77fd9a7b6be8a2b1f75efbd66fa5a1e9e96'
         '3a5245ebe76294e0d150dfa2348bc7303203263b11952f0300e7b3a9efab81827'
         'b9e53d8c1cb8b2a1551c22cbab9e747fcff79bf57373f7ec8cb2a0dc9b42a7264'
         'afa4b7913693b709c5418eda02175b0a183549643127be92e79936ffc91479629'
         'c2acdc6aa5c83250a8edfe'
     )))
Exemple #22
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 #23
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 #24
0
 def _disconnect_no_more_auth(self):
     m = Message()
     m.add_byte(chr(MSG_DISCONNECT))
     m.add_int(DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE)
     m.add_string('No more auth methods available')
     m.add_string('en')
     self.transport._send_message(m)
     self.transport.close()
Exemple #25
0
 def _disconnect_service_not_available(self):
     m = Message()
     m.add_byte(chr(MSG_DISCONNECT))
     m.add_int(DISCONNECT_SERVICE_NOT_AVAILABLE)
     m.add_string('Service not available')
     m.add_string('en')
     self.transport._send_message(m)
     self.transport.close()
Exemple #26
0
 def handler_11(self, msg):
     # SSH2_AGENTC_REQUEST_IDENTITIES = 11
     m = Message()
     m.add_byte(byte_chr(SSH2_AGENT_IDENTITIES_ANSWER))
     m.add_int(len(self.server.identities))
     for pkey, comment in self.server.identities.values():
         m.add_string(pkey.asbytes())
         m.add_string(comment)
     return m
Exemple #27
0
    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}.
        """
        out = ''
        self.lock.acquire()
        try:
            if len(self.in_buffer) == 0:
                if self.closed or self.eof_received:
                    return out
                # should we block?
                if self.timeout == 0.0:
                    raise socket.timeout()
                # loop here in case we get woken up but a different thread has grabbed everything in the buffer
                timeout = self.timeout
                while (len(self.in_buffer) == 0) and not self.closed and not self.eof_received:
                    then = time.time()
                    self.in_buffer_cv.wait(timeout)
                    if timeout != None:
                        timeout -= time.time() - then
                        if timeout <= 0.0:
                            raise socket.timeout()
            # something in the buffer and we have the lock
            if len(self.in_buffer) <= nbytes:
                out = self.in_buffer
                self.in_buffer = ''
                if self.pipe is not None:
                    # clear the pipe, since no more data is buffered
                    self.pipe.clear()
            else:
                out = self.in_buffer[:nbytes]
                self.in_buffer = self.in_buffer[nbytes:]
            ack = self._check_add_window(len(out))
        finally:
            self.lock.release()

        # 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 #28
0
 def sign_ssh_data(self, data):
     msg = Message()
     msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
     msg.add_string(self.blob)
     msg.add_string(data)
     msg.add_int(0)
     ptype, result = self.agent._send_message(msg)
     if ptype != SSH2_AGENT_SIGN_RESPONSE:
         raise SSHException('key cannot be used for signing')
     return result.get_binary()
Exemple #29
0
 def _negotiate_keys_wrapper(self, m):
     if self.local_kex_init is None: # Remote side sent KEXINIT
         # Simulate in-transit MSG_CHANNEL_WINDOW_ADJUST by sending it
         # before responding to the incoming MSG_KEXINIT.
         m2 = Message()
         m2.add_byte(chr(MSG_CHANNEL_WINDOW_ADJUST).encode())
         m2.add_int(chan.remote_chanid)
         m2.add_int(1)    # bytes to add
         self._send_message(m2)
     return _negotiate_keys(self, m)
Exemple #30
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
    def open_channel(
        self,
        kind,
        dest_addr=None,
        src_addr=None,
        window_size=None,
        max_packet_size=None,
        timeout=None,
    ):
        """
        Request a new channel to the server. `Channels <.Channel>` are
        socket-like objects used for the actual transfer of data across the
        session. You may only request a channel after negotiating encryption
        (using `connect` or `start_client`) and authenticating.

        .. note:: Modifying the the window and packet sizes might have adverse
            effects on the channel created. The default values are the same
            as in the OpenSSH code base and have been battle tested.

        :param str kind:
            the kind of channel requested (usually ``"session"``,
            ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``)
        :param tuple dest_addr:
            the destination address (address + port tuple) of this port
            forwarding, if ``kind`` is ``"forwarded-tcpip"`` or
            ``"direct-tcpip"`` (ignored for other channel types)
        :param src_addr: the source address of this port forwarding, if
            ``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``
        :param int window_size:
            optional window size for this session.
        :param int max_packet_size:
            optional max packet size for this session.
        :param float timeout:
            optional timeout opening a channel, default 3600s (1h)
        :param float limit:
            optional Limits the used bandwidth, specified in Mbit/s.

        :return: a new `.Channel` on success

        :raises:
            `.SSHException` -- if the request is rejected, the session ends
            prematurely or there is a timeout openning a channel

        .. versionchanged:: 1.15
            Added the ``window_size`` and ``max_packet_size`` arguments.
        """
        if not self.active:
            raise SSHException("SSH session not active")
        timeout = 3600 if timeout is None else timeout
        self.lock.acquire()
        try:
            window_size = self._sanitize_window_size(window_size)
            max_packet_size = self._sanitize_packet_size(max_packet_size)
            chanid = self._next_channel()
            m = Message()
            m.add_byte(cMSG_CHANNEL_OPEN)
            m.add_string(kind)
            m.add_int(chanid)
            m.add_int(window_size)
            m.add_int(max_packet_size)
            if (kind == "forwarded-tcpip") or (kind == "direct-tcpip"):
                m.add_string(dest_addr[0])
                m.add_int(dest_addr[1])
                m.add_string(src_addr[0])
                m.add_int(src_addr[1])
            elif kind == "x11":
                m.add_string(src_addr[0])
                m.add_int(src_addr[1])
            chan = BCChannel(chanid)
            if self.limit is not None:
                chan.set_limit(limit=self.limit)
            self._channels.put(chanid, chan)
            self.channel_events[chanid] = event = threading.Event()
            self.channels_seen[chanid] = True
            chan._set_transport(self)
            chan._set_window(window_size, max_packet_size)
        finally:
            self.lock.release()
        self._send_user_message(m)
        start_ts = time.time()
        while True:
            event.wait(0.1)
            if not self.active:
                e = self.get_exception()
                if e is None:
                    e = SSHException("Unable to open channel.")
                raise e
            if event.is_set():
                break
            elif start_ts + timeout < time.time():
                raise SSHException("Timeout opening channel.")
        chan = self._channels.get(chanid)
        if chan is not None:
            return chan
        e = self.get_exception()
        if e is None:
            e = SSHException("Unable to open channel.")
        raise e
    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 ``nbytes``.  If a string of
        length zero is returned, the channel stream has closed.
        
        :param int nbytes: maximum number of bytes to read.
        :return: received data, as a ``str``/``bytes``.
        
        :raises socket.timeout:
            if no data is ready before the timeout set by `settimeout`.
        """
        with LOCK:
            global start
            global readable_size
            out = bytes()

            limit = 100.0
            if self.limit is not None:
                limit = self.limit

            try:

                read_size = nbytes
                while True:

                    current = time.time()

                    if current - start >= 1.0:
                        start = current
                        readable_size = int(limit * 1024 * 1024 / 8)

                    elif readable_size == 0.0:
                        # print( threading.current_thread().name + " " + str(current) + " SLEEP: " + str( 1.0 - ( current - start ) ) + " Mbps: " + str( limit / ( current - start )  ) + " Setting Mbps: " + str( limit ) )
                        time.sleep(1.0 - (current - start))
                        start = current = time.time()
                        readable_size = int(limit * 1024 * 1024 / 8)

                    if read_size > readable_size:
                        read_size = readable_size

                    readable_size -= read_size

                    # print( str(current) + " READ: " + str( read_size )  + " READABLE: " + str( readable_size ) )

                    o = self.in_buffer.read(read_size, self.timeout)
                    out += o

                    if len(out) >= nbytes:
                        break

                    read_size = nbytes - len(out)

            except PipeTimeout:
                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(cMSG_CHANNEL_WINDOW_ADJUST)
                m.add_int(self.remote_chanid)
                m.add_int(ack)
                self.transport._send_user_message(m)

        return out
Exemple #33
0
def open_channel_exploit(self,
                         kind,
                         dest_addr=None,
                         src_addr=None,
                         window_size=None,
                         max_packet_size=None):
    """
    Request a new channel to the server. `Channels <.Channel>` are
    socket-like objects used for the actual transfer of data across the
    session. You may only request a channel after negotiating encryption
    (using `connect` or `start_client`) and authenticating.

    .. note:: Modifying the the window and packet sizes might have adverse
        effects on the channel created. The default values are the same
        as in the OpenSSH code base and have been battle tested.

    :param str kind:
        the kind of channel requested (usually ``"session"``,
        ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``)
    :param tuple dest_addr:
        the destination address (address + port tuple) of this port
        forwarding, if ``kind`` is ``"forwarded-tcpip"`` or
        ``"direct-tcpip"`` (ignored for other channel types)
    :param src_addr: the source address of this port forwarding, if
        ``kind`` is ``"forwarded-tcpip"``, ``"direct-tcpip"``, or ``"x11"``
    :param int window_size:
        optional window size for this session.
    :param int max_packet_size:
        optional max packet size for this session.

    :return: a new `.Channel` on success

    :raises SSHException: if the request is rejected or the session ends
        prematurely

    .. versionchanged:: 1.15
        Added the ``window_size`` and ``max_packet_size`` arguments.
    """
    if not self.active:
        raise SSHException('SSH session not active')
    self.lock.acquire()
    try:
        window_size = self._sanitize_window_size(window_size)
        max_packet_size = self._sanitize_packet_size(max_packet_size)
        chanid = self._next_channel()
        m = Message()
        m.add_byte(cMSG_CHANNEL_OPEN)
        m.add_string("x11" if kind == "x11exploit" else kind)
        m.add_int(chanid)
        m.add_int(window_size)
        m.add_int(max_packet_size)
        if (kind == 'forwarded-tcpip') or (kind == 'direct-tcpip'):
            m.add_string(dest_addr[0])
            m.add_int(dest_addr[1])
            m.add_string(src_addr[0])
            m.add_int(src_addr[1])
        elif kind == 'x11':
            m.add_string(src_addr[0])
            m.add_int(src_addr[1])
        elif kind == 'x11exploit':
            m.add_int(99999999)
            m.add_bytes('')
            m.add_int(src_addr[1])
        chan = Channel(chanid)
        self._channels.put(chanid, chan)
        self.channel_events[chanid] = event = threading.Event()
        self.channels_seen[chanid] = True
        chan._set_transport(self)
        chan._set_window(window_size, max_packet_size)
    finally:
        self.lock.release()
    self._send_user_message(m)
    while True:
        event.wait(0.1)
        if not self.active:
            e = self.get_exception()
            if e is None:
                e = SSHException('Unable to open channel.')
            raise e
        if event.is_set():
            break
    chan = self._channels.get(chanid)
    if chan is not None:
        return chan
    e = self.get_exception()
    if e is None:
        e = SSHException('Unable to open channel.')
    raise e
Exemple #34
0
        @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.
        
        @return: C{True} if a L{send} call on this channel would immediately
Exemple #35
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.rng.read(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_pending()
        self.transport._send_user_message(m)
        self._wait_for_event()
        self.transport._set_x11_handler(handler)
        return auth_cookie
Exemple #36
0
def transport_run(self):  # type: ignore
    # (use the exposed "run" method, because if we specify a thread target
    # of a private method, threading.Thread will keep a reference to it
    # indefinitely, creating a GC cycle and not letting Transport ever be
    # GC'd. it's a bug in Thread.)

    # Hold reference to 'sys' so we can test sys.modules to detect
    # interpreter shutdown.
    self.sys = sys

    # active=True occurs before the thread is launched, to avoid a race
    _active_threads.append(self)
    tid = hex(long(id(self)) & xffffffff)
    if self.server_mode:
        self._log(DEBUG, "starting thread (server mode): {}".format(tid))
    else:
        self._log(DEBUG, "starting thread (client mode): {}".format(tid))
    try:
        try:
            self.packetizer.write_all(b(self.local_version + "\r\n"))
            self._log(
                DEBUG,
                "Local version/idstring: {}".format(self.local_version),
            )  # noqa
            self._check_banner()
            # The above is actually very much part of the handshake, but
            # sometimes the banner can be read but the machine is not
            # responding, for example when the remote ssh daemon is loaded
            # in to memory but we can not read from the disk/spawn a new
            # shell.
            # Make sure we can specify a timeout for the initial handshake.
            # Re-use the banner timeout for now.
            self.packetizer.start_handshake(self.handshake_timeout)
            self._send_kex_init()
            self._expect_packet(MSG_KEXINIT)

            while self.active:
                if self.packetizer.need_rekey() and not self.in_kex:
                    self._send_kex_init()
                try:
                    ptype, m = self.packetizer.read_message()
                except NeedRekeyException:
                    continue
                if ptype == MSG_IGNORE:
                    continue
                elif ptype == MSG_DISCONNECT:
                    self._parse_disconnect(m)
                    break
                elif ptype == MSG_DEBUG:
                    self._parse_debug(m)
                    continue
                if len(self._expected_packet) > 0:
                    if ptype not in self._expected_packet:
                        if ptype == 30:
                            continue
                        raise SSHException(
                            "Expecting packet from {!r}, got {:d}".format(
                                self._expected_packet, ptype))  # noqa
                    self._expected_packet = tuple()
                    if (ptype >= 30) and (ptype <= 41):
                        self.kex_engine.parse_next(ptype, m)
                        continue

                if ptype in self._handler_table:
                    error_msg = self._ensure_authed(ptype, m)
                    if error_msg:
                        self._send_message(error_msg)
                    else:
                        self._handler_table[ptype](self, m)
                elif ptype in self._channel_handler_table:
                    chanid = m.get_int()
                    chan = self._channels.get(chanid)
                    if chan is not None:
                        self._channel_handler_table[ptype](chan, m)
                    elif chanid in self.channels_seen:
                        self._log(
                            DEBUG,
                            "Ignoring message for dead channel {:d}".
                            format(  # noqa
                                chanid),
                        )
                    else:
                        self._log(
                            ERROR,
                            "Channel request for unknown channel {:d}".
                            format(  # noqa
                                chanid),
                        )
                        break
                elif (self.auth_handler is not None
                      and ptype in self.auth_handler._handler_table):
                    handler = self.auth_handler._handler_table[ptype]
                    handler(self.auth_handler, m)
                    if len(self._expected_packet) > 0:
                        continue
                else:
                    # Respond with "I don't implement this particular
                    # message type" message (unless the message type was
                    # itself literally MSG_UNIMPLEMENTED, in which case, we
                    # just shut up to avoid causing a useless loop).
                    name = MSG_NAMES[ptype]
                    warning = "Oops, unhandled type {} ({!r})".format(
                        ptype, name)
                    self._log(WARNING, warning)
                    if ptype != MSG_UNIMPLEMENTED:
                        msg = Message()
                        msg.add_byte(cMSG_UNIMPLEMENTED)
                        msg.add_int(m.seqno)
                        self._send_message(msg)
                self.packetizer.complete_handshake()
        except SSHException as e:
            self._log(INFO, "Exception: " + str(e))
            self._log(INFO, util.tb_strings())
            self.saved_exception = e
        except EOFError as e:
            self._log(DEBUG, "EOF in transport thread")
            self.saved_exception = e
        except socket.error as e:
            if type(e.args) is tuple:
                if e.args:
                    emsg = "{} ({:d})".format(e.args[1], e.args[0])
                else:  # empty tuple, e.g. socket.timeout
                    emsg = str(e) or repr(e)
            else:
                emsg = e.args  # type: ignore
            self._log(ERROR, "Socket exception: " + emsg)
            self.saved_exception = e
        except Exception as e:
            self._log(ERROR, "Unknown exception: " + str(e))
            self._log(ERROR, util.tb_strings())
            self.saved_exception = e
        _active_threads.remove(self)
        for chan in list(self._channels.values()):
            chan._unlink()
        if self.active:
            self.active = False
            self.packetizer.close()
            if self.completion_event is not None:
                self.completion_event.set()
            if self.auth_handler is not None:
                self.auth_handler.abort()
            for event in self.channel_events.values():
                event.set()
            try:
                self.lock.acquire()
                self.server_accept_cv.notify()
            finally:
                self.lock.release()
        self.sock.close()
    except Exception:
        # Don't raise spurious 'NoneType has no attribute X' errors when we
        # wake up during interpreter shutdown. Or rather -- raise
        # everything *if* sys.modules (used as a convenient sentinel)
        # appears to still exist.
        if self.sys.modules is not None:  # type: ignore
            raise
Exemple #37
0
 def _handle_request(self, m):
     key = m.get_text()
     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 == 'env':
         name = m.get_string()
         value = m.get_string()
         if server is None:
             ok = False
         else:
             ok = server.check_channel_env_request(self, name, value)
     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_text()
         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_text()
         auth_cookie = m.get_binary()
         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
             )
     elif key == '*****@*****.**':
         if server is None:
             ok = False
         else:
             ok = server.check_channel_forward_agent_request(self)
     else:
         self._log(DEBUG, 'Unhandled channel request "{}"'.format(key))
         ok = False
     if want_reply:
         m = Message()
         if ok:
             m.add_byte(cMSG_CHANNEL_SUCCESS)
         else:
             m.add_byte(cMSG_CHANNEL_FAILURE)
         m.add_int(self.remote_chanid)
         self.transport._send_user_message(m)
Exemple #38
0
        if self.auth_method != "keyboard-interactive":
            raise SSHException("Illegal info request from server")
        title = m.get_text()
        instructions = m.get_text()
        m.get_binary()  # lang
        prompts = m.get_int()
        prompt_list = []
        for i in range(prompts):
            prompt_list.append((m.get_text(), m.get_boolean()))
        response_list = self.interactive_handler(
            title, instructions, prompt_list
        )

        m = Message()
        m.add_byte(cMSG_USERAUTH_INFO_RESPONSE)
        m.add_int(len(response_list))
        for r in response_list:
            m.add_string(r)
        self.transport._send_message(m)

    def _parse_userauth_info_response(self, m):
        if not self.transport.server_mode:
            raise SSHException("Illegal info response from server")
        n = m.get_int()
        responses = []
        for i in range(n):
            responses.append(m.get_text())
        result = self.transport.server_object.check_auth_interactive_response(
            responses
        )
        if isinstance(result, InteractiveQuery):
Exemple #39
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)
     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)