Exemple #1
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 #2
0
 def test_4_misc(self):
     msg = Message(self.__d)
     self.assertEquals(msg.get_int(), 5)
     self.assertEquals(msg.get_mpint(), 0x1122334455L)
     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 #3
0
 def test_4_misc(self):
     msg = Message(self.__d)
     self.assertEqual(msg.get_int(), 5)
     self.assertEqual(msg.get_int(), 0x1122334455)
     self.assertEqual(msg.get_int(), 0xf00000000000000000)
     self.assertEqual(msg.get_so_far(), self.__d[:29])
     self.assertEqual(msg.get_remainder(), self.__d[29:])
     msg.rewind()
     self.assertEqual(msg.get_int(), 5)
     self.assertEqual(msg.get_so_far(), self.__d[:4])
     self.assertEqual(msg.get_remainder(), self.__d[4:])
 def test_4_misc(self):
     msg = Message(self.__d)
     self.assertEqual(msg.get_int(), 5)
     self.assertEqual(msg.get_int(), 0x1122334455)
     self.assertEqual(msg.get_int(), 0xf00000000000000000)
     self.assertEqual(msg.get_so_far(), self.__d[:29])
     self.assertEqual(msg.get_remainder(), self.__d[29:])
     msg.rewind()
     self.assertEqual(msg.get_int(), 5)
     self.assertEqual(msg.get_so_far(), self.__d[:4])
     self.assertEqual(msg.get_remainder(), self.__d[4:])
Exemple #5
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()
         self._lock.acquire()
         try:
             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]
         finally:
             self._lock.release()
         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 #6
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 #7
0
 def test_misc(self):
     msg = Message(self.__d)
     self.assertEqual(msg.get_int(), 5)
     self.assertEqual(msg.get_boolean(), True)
     self.assertEqual(msg.get_text(), 'cat')
     self.assertEqual(msg.get_so_far(), self.__d[:12])
     self.assertEqual(msg.get_remainder(), self.__d[12:])
Exemple #8
0
 def test_handle_11(self):
     # Test handling a SSH2_AGENTC_REQUEST_IDENTITIES
     msg = Message()
     msg.add_byte(byte_chr(11))
     mtype, msg = self.send(msg)
     self.assertEqual(mtype, 12)
     # There should be one identity in the list
     self.assertEqual(msg.get_int(), 1)
     # It should be our identity
     pkey, comment = list(self.agent.identities.values())[0]
     self.assertEqual(msg.get_binary(), pkey.asbytes())
     self.assertEqual(msg.get_string(), b'id_rsa_test')
Exemple #9
0
    def test_2_decode(self):
        msg = Message(self.__a)
        self.assertEquals(msg.get_int(), 23)
        self.assertEquals(msg.get_int(), 123789456)
        self.assertEquals(msg.get_string(), b'q')
        self.assertEquals(msg.get_string(), b'hello')
        self.assertEquals(msg.get_string(), b'x' * 1000)

        msg = Message(self.__b)
        self.assertEquals(msg.get_boolean(), True)
        self.assertEquals(msg.get_boolean(), False)
        self.assertEquals(msg.get_byte(), b'\xf3')
        self.assertEquals(msg.get_bytes(2), b'\x00\x3f')
        self.assertEquals(msg.get_list(), [b'huey', b'dewey', b'louie'])

        msg = Message(self.__c)
        self.assertEquals(msg.get_int64(), 5)
        self.assertEquals(msg.get_int64(), 0xf5e4d3c2b109)
        self.assertEquals(msg.get_mpint(), 17)
        self.assertEquals(msg.get_mpint(), 0xf5e4d3c2b109)
        self.assertEquals(msg.get_mpint(), -0x65e4d3c2b109)
Exemple #10
0
    def test_2_decode(self):
        msg = Message(self.__a)
        self.assertEquals(msg.get_int(), 23)
        self.assertEquals(msg.get_int(), 123789456)
        self.assertEquals(msg.get_string(), "q")
        self.assertEquals(msg.get_string(), "hello")
        self.assertEquals(msg.get_string(), "x" * 1000)

        msg = Message(self.__b)
        self.assertEquals(msg.get_boolean(), True)
        self.assertEquals(msg.get_boolean(), False)
        self.assertEquals(msg.get_byte(), "\xf3")
        self.assertEquals(msg.get_bytes(2), "\x00\x3f")
        self.assertEquals(msg.get_list(), ["huey", "dewey", "louie"])

        msg = Message(self.__c)
        self.assertEquals(msg.get_int64(), 5)
        self.assertEquals(msg.get_int64(), 0xF5E4D3C2B109L)
        self.assertEquals(msg.get_mpint(), 17)
        self.assertEquals(msg.get_mpint(), 0xF5E4D3C2B109L)
        self.assertEquals(msg.get_mpint(), -0x65E4D3C2B109L)
Exemple #11
0
    def test_decode(self):
        msg = Message(self.__a)
        self.assertEqual(msg.get_int(), 23)
        self.assertEqual(msg.get_int(), 123789456)
        self.assertEqual(msg.get_text(), 'q')
        self.assertEqual(msg.get_text(), 'hello')
        self.assertEqual(msg.get_text(), 'x' * 1000)

        msg = Message(self.__b)
        self.assertEqual(msg.get_boolean(), True)
        self.assertEqual(msg.get_boolean(), False)
        self.assertEqual(msg.get_byte(), byte_chr(0xf3))
        self.assertEqual(msg.get_bytes(2), zero_byte + byte_chr(0x3f))
        self.assertEqual(msg.get_list(), ['huey', 'dewey', 'louie'])

        msg = Message(self.__c)
        self.assertEqual(msg.get_int64(), 5)
        self.assertEqual(msg.get_int64(), 0xf5e4d3c2b109)
        self.assertEqual(msg.get_mpint(), 17)
        self.assertEqual(msg.get_mpint(), 0xf5e4d3c2b109)
        self.assertEqual(msg.get_mpint(), -0x65e4d3c2b109)
Exemple #12
0
    def test_2_decode(self):
        msg = Message(self.__a)
        self.assertEqual(msg.get_int(), 23)
        self.assertEqual(msg.get_int(), 123789456)
        self.assertEqual(msg.get_text(), "q")
        self.assertEqual(msg.get_text(), "hello")
        self.assertEqual(msg.get_text(), "x" * 1000)

        msg = Message(self.__b)
        self.assertEqual(msg.get_boolean(), True)
        self.assertEqual(msg.get_boolean(), False)
        self.assertEqual(msg.get_byte(), byte_chr(0xf3))
        self.assertEqual(msg.get_bytes(2), zero_byte + byte_chr(0x3f))
        self.assertEqual(msg.get_list(), ["huey", "dewey", "louie"])

        msg = Message(self.__c)
        self.assertEqual(msg.get_int64(), 5)
        self.assertEqual(msg.get_int64(), 0xf5e4d3c2b109)
        self.assertEqual(msg.get_mpint(), 17)
        self.assertEqual(msg.get_mpint(), 0xf5e4d3c2b109)
        self.assertEqual(msg.get_mpint(), -0x65e4d3c2b109)
    def test_2_decode(self):
        msg = Message(self.__a)
        self.assertEqual(msg.get_int(), 23)
        self.assertEqual(msg.get_int(), 123789456)
        self.assertEqual(msg.get_text(), 'q')
        self.assertEqual(msg.get_text(), 'hello')
        self.assertEqual(msg.get_text(), 'x' * 1000)

        msg = Message(self.__b)
        self.assertEqual(msg.get_boolean(), True)
        self.assertEqual(msg.get_boolean(), False)
        self.assertEqual(msg.get_byte(), byte_chr(0xf3))
        self.assertEqual(msg.get_bytes(2), zero_byte + byte_chr(0x3f))
        self.assertEqual(msg.get_list(), ['huey', 'dewey', 'louie'])

        msg = Message(self.__c)
        self.assertEqual(msg.get_int64(), 5)
        self.assertEqual(msg.get_int64(), 0xf5e4d3c2b109)
        self.assertEqual(msg.get_mpint(), 17)
        self.assertEqual(msg.get_mpint(), 0xf5e4d3c2b109)
        self.assertEqual(msg.get_mpint(), -0x65e4d3c2b109)
Exemple #14
0
    def test_2_decode(self):
        msg = Message(self.__a)
        self.assertEquals(msg.get_int(), 23)
        self.assertEquals(msg.get_int(), 123789456)
        self.assertEquals(msg.get_string(), 'q')
        self.assertEquals(msg.get_string(), 'hello')
        self.assertEquals(msg.get_string(), 'x' * 1000)

        msg = Message(self.__b)
        self.assertEquals(msg.get_boolean(), True)
        self.assertEquals(msg.get_boolean(), False)
        self.assertEquals(msg.get_byte(), '\xf3')
        self.assertEquals(msg.get_bytes(2), '\x00\x3f')
        self.assertEquals(msg.get_list(), ['huey', 'dewey', 'louie'])

        msg = Message(self.__c)
        self.assertEquals(msg.get_int64(), 5)
        self.assertEquals(msg.get_int64(), 0xf5e4d3c2b109L)
        self.assertEquals(msg.get_mpint(), 17)
        self.assertEquals(msg.get_mpint(), 0xf5e4d3c2b109L)
        self.assertEquals(msg.get_mpint(), -0x65e4d3c2b109L)
    def _read_response(self, waitfor=None):
        while True:
            try:
                t, data = self._read_packet()
            except EOFError as e:
                raise SSHException("Server connection dropped: {}".format(e))
            msg = Message(data)
            num = msg.get_int()
            self._lock.acquire()
            try:
                if num not in self._expecting:
                    # might be response for a file that was closed before
                    # responses came back
                    self._log(DEBUG, "Unexpected response #{}".format(num))
                    if waitfor is None:
                        # just doing a single check
                        break
                    continue
                fileobj = self._expecting[num]
                del self._expecting[num]
            finally:
                self._lock.release()
            if num == waitfor:
                # synchronous
                if t == CMD_STATUS:
                    self._convert_status(msg)
                return t, msg

            # can not rewrite this to deal with E721, either as a None check
            # nor as not an instance of None or NoneType
            if fileobj is not type(None):  # noqa
                fileobj._async_response(t, msg, num)
            if waitfor is None:
                # just doing a single check
                break
        return None, None
Exemple #16
0
    def _read_response(self, waitfor=None):
        while True:
            try:
                t, data = self._read_packet()
            except EOFError as e:
                raise SSHException('Server connection dropped: {}'.format(e))
            msg = Message(data)
            num = msg.get_int()
            self._lock.acquire()
            try:
                if num not in self._expecting:
                    # might be response for a file that was closed before
                    # responses came back
                    self._log(DEBUG, 'Unexpected response #{}'.format(num))
                    if waitfor is None:
                        # just doing a single check
                        break
                    continue
                fileobj = self._expecting[num]
                del self._expecting[num]
            finally:
                self._lock.release()
            if num == waitfor:
                # synchronous
                if t == CMD_STATUS:
                    self._convert_status(msg)
                return t, msg

            # can not rewrite this to deal with E721, either as a None check
            # nor as not an instance of None or NoneType
            if fileobj is not type(None):  # noqa
                fileobj._async_response(t, msg, num)
            if waitfor is None:
                # just doing a single check
                break
        return None, None
    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:
                    result = AUTH_FAILED
                    self._send_auth_result(username, method, result)
                    return
                # 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
            # 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)
        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
            result = AUTH_SUCCESSFUL
            self.transport.server_object.check_auth_gssapi_keyex(
                username, result)
        else:
            result = self.transport.server_object.check_auth_none(username)
        # okay, send result
        self._send_auth_result(username, method, result)
 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 #19
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._log(
            DEBUG,
            "Auth request (type={}) service={}, username={}".format(
                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._log(
                WARNING,
                "Auth rejected because the client attempted to change username in mid-flight",  # noqa
            )
            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._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._log(INFO, "Auth rejected: public key: {}".format(str(e)))
                key = None
            except Exception as e:
                msg = (
                    "Auth rejected: unsupported or mangled public key ({}: {})"
                )  # noqa
                self._log(INFO, msg.format(e.__class__.__name__, e))
                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._log(INFO, "Auth rejected: invalid signature")
                    result = AUTH_FAILED
        elif method == "keyboard-interactive":
            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._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._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
            m = Message()
            m.add_byte(cMSG_USERAUTH_GSSAPI_RESPONSE)
            m.add_bytes(supported_mech)
            self.transport.auth_handler = GssapiWithMicAuthHandler(
                self, sshgss
            )
            self.transport._expected_packet = (
                MSG_USERAUTH_GSSAPI_TOKEN,
                MSG_USERAUTH_REQUEST,
                MSG_SERVICE_REQUEST,
            )
            self.transport._send_message(m)
            return
        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
            result = AUTH_SUCCESSFUL
            self.transport.server_object.check_auth_gssapi_keyex(
                username, result
            )
        else:
            result = self.transport.server_object.check_auth_none(username)
        # okay, send result
        self._send_auth_result(username, method, result)
Exemple #20
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 #21
0
    def _parse_signing_key_data(self, data, password):
        from paramiko.transport import Transport
        # We may eventually want this to be usable for other key types, as
        # OpenSSH moves to it, but for now this is just for Ed25519 keys.
        # This format is described here:
        # https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
        # The description isn't totally complete, and I had to refer to the
        # source for a full implementation.
        message = Message(data)
        if message.get_bytes(len(OPENSSH_AUTH_MAGIC)) != OPENSSH_AUTH_MAGIC:
            raise SSHException("Invalid key")

        ciphername = message.get_text()
        kdfname = message.get_text()
        kdfoptions = message.get_binary()
        num_keys = message.get_int()

        if kdfname == "none":
            # kdfname of "none" must have an empty kdfoptions, the ciphername
            # must be "none"
            if kdfoptions or ciphername != "none":
                raise SSHException("Invalid key")
        elif kdfname == "bcrypt":
            if not password:
                raise PasswordRequiredException(
                    "Private key file is encrypted"
                )
            kdf = Message(kdfoptions)
            bcrypt_salt = kdf.get_binary()
            bcrypt_rounds = kdf.get_int()
        else:
            raise SSHException("Invalid key")

        if ciphername != "none" and ciphername not in Transport._cipher_info:
            raise SSHException("Invalid key")

        public_keys = []
        for _ in range(num_keys):
            pubkey = Message(message.get_binary())
            if pubkey.get_text() != "ssh-ed25519":
                raise SSHException("Invalid key")
            public_keys.append(pubkey.get_binary())

        private_ciphertext = message.get_binary()
        if ciphername == "none":
            private_data = private_ciphertext
        else:
            cipher = Transport._cipher_info[ciphername]
            key = bcrypt.kdf(
                password=password,
                salt=bcrypt_salt,
                desired_key_bytes=cipher["key-size"] + cipher["block-size"],
                rounds=bcrypt_rounds,
                # We can't control how many rounds are on disk, so no sense
                # warning about it.
                ignore_few_rounds=True,
            )
            decryptor = Cipher(
                cipher["class"](key[:cipher["key-size"]]),
                cipher["mode"](key[cipher["key-size"]:]),
                backend=default_backend()
            ).decryptor()
            private_data = (
                decryptor.update(private_ciphertext) + decryptor.finalize()
            )

        message = Message(unpad(private_data))
        if message.get_int() != message.get_int():
            raise SSHException("Invalid key")

        signing_keys = []
        for i in range(num_keys):
            if message.get_text() != "ssh-ed25519":
                raise SSHException("Invalid key")
            # A copy of the public key, again, ignore.
            public = message.get_binary()
            key_data = message.get_binary()
            # The second half of the key data is yet another copy of the public
            # key...
            signing_key = nacl.signing.SigningKey(key_data[:32])
            # Verify that all the public keys are the same...
            assert (
                signing_key.verify_key.encode() == public == public_keys[i] ==
                key_data[32:]
            )
            signing_keys.append(signing_key)
            # Comment, ignore.
            message.get_binary()

        if len(signing_keys) != 1:
            raise SSHException("Invalid key")
        return signing_keys[0]
Exemple #22
0
    def _parse_signing_key_data(self, data, password):
        from paramiko.transport import Transport

        # We may eventually want this to be usable for other key types, as
        # OpenSSH moves to it, but for now this is just for Ed25519 keys.
        # This format is described here:
        # https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
        # The description isn't totally complete, and I had to refer to the
        # source for a full implementation.
        message = Message(data)
        if message.get_bytes(len(OPENSSH_AUTH_MAGIC)) != OPENSSH_AUTH_MAGIC:
            raise SSHException("Invalid key")

        ciphername = message.get_text()
        kdfname = message.get_text()
        kdfoptions = message.get_binary()
        num_keys = message.get_int()

        if kdfname == "none":
            # kdfname of "none" must have an empty kdfoptions, the ciphername
            # must be "none"
            if kdfoptions or ciphername != "none":
                raise SSHException("Invalid key")
        elif kdfname == "bcrypt":
            if not password:
                raise PasswordRequiredException(
                    "Private key file is encrypted"
                )
            kdf = Message(kdfoptions)
            bcrypt_salt = kdf.get_binary()
            bcrypt_rounds = kdf.get_int()
        else:
            raise SSHException("Invalid key")

        if ciphername != "none" and ciphername not in Transport._cipher_info:
            raise SSHException("Invalid key")

        public_keys = []
        for _ in range(num_keys):
            pubkey = Message(message.get_binary())
            if pubkey.get_text() != "ssh-ed25519":
                raise SSHException("Invalid key")
            public_keys.append(pubkey.get_binary())

        private_ciphertext = message.get_binary()
        if ciphername == "none":
            private_data = private_ciphertext
        else:
            cipher = Transport._cipher_info[ciphername]
            key = bcrypt.kdf(
                password=b(password),
                salt=bcrypt_salt,
                desired_key_bytes=cipher["key-size"] + cipher["block-size"],
                rounds=bcrypt_rounds,
                # We can't control how many rounds are on disk, so no sense
                # warning about it.
                ignore_few_rounds=True,
            )
            decryptor = Cipher(
                cipher["class"](key[: cipher["key-size"]]),
                cipher["mode"](key[cipher["key-size"] :]),
                backend=default_backend(),
            ).decryptor()
            private_data = (
                decryptor.update(private_ciphertext) + decryptor.finalize()
            )

        message = Message(unpad(private_data))
        if message.get_int() != message.get_int():
            raise SSHException("Invalid key")

        signing_keys = []
        for i in range(num_keys):
            if message.get_text() != "ssh-ed25519":
                raise SSHException("Invalid key")
            # A copy of the public key, again, ignore.
            public = message.get_binary()
            key_data = message.get_binary()
            # The second half of the key data is yet another copy of the public
            # key...
            signing_key = nacl.signing.SigningKey(key_data[:32])
            # Verify that all the public keys are the same...
            assert (
                signing_key.verify_key.encode()
                == public
                == public_keys[i]
                == key_data[32:]
            )
            signing_keys.append(signing_key)
            # Comment, ignore.
            message.get_binary()

        if len(signing_keys) != 1:
            raise SSHException("Invalid key")
        return signing_keys[0]
Exemple #23
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 #24
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._log(
            DEBUG,
            "Auth request (type={}) service={}, username={}".format(
                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._log(
                WARNING,
                "Auth rejected because the client attempted to change username in mid-flight",  # noqa
            )
            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._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._log(INFO, "Auth rejected: public key: {}".format(str(e)))
                key = None
            except Exception as e:
                msg = (
                    "Auth rejected: unsupported or mangled public key ({}: {})"
                )  # noqa
                self._log(INFO, msg.format(e.__class__.__name__, e))
                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._log(INFO, "Auth rejected: invalid signature")
                    result = AUTH_FAILED
        elif method == "keyboard-interactive":
            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._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._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
            m = Message()
            m.add_byte(cMSG_USERAUTH_GSSAPI_RESPONSE)
            m.add_bytes(supported_mech)
            self.transport.auth_handler = GssapiWithMicAuthHandler(
                self, sshgss)
            self.transport._expected_packet = (
                MSG_USERAUTH_GSSAPI_TOKEN,
                MSG_USERAUTH_REQUEST,
                MSG_SERVICE_REQUEST,
            )
            self.transport._send_message(m)
            return
        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
            result = AUTH_SUCCESSFUL
            self.transport.server_object.check_auth_gssapi_keyex(
                username, result)
        else:
            result = self.transport.server_object.check_auth_none(username)
        # okay, send result
        self._send_auth_result(username, method, result)
Exemple #25
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 (dependent 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 #26
0
    def _read_private_key_new_format(data, password):
        """
        Read the new OpenSSH SSH2 private key format available
        since OpenSSH version 6.5
        Reference:
        https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
        https://coolaj86.com/articles/the-openssh-private-key-format/
        """
        message = Message(data)
        OPENSSH_AUTH_MAGIC = b"openssh-key-v1\x00"
        if message.get_bytes(len(OPENSSH_AUTH_MAGIC)) != OPENSSH_AUTH_MAGIC:
            raise SSHException("unexpected OpenSSH key header encountered")

        cipher = message.get_text()
        kdfname = message.get_text()
        kdfoptions = message.get_binary()
        num_keys = message.get_int()

        if num_keys > 1:
            raise SSHException(
                "unsupported: private keyfile has multiple keys")

        public_data = message.get_binary()
        privkey_blob = message.get_binary()

        pub_keytype = Message(public_data).get_text()

        if kdfname == "none":
            if kdfoptions or cipher != "none":
                raise SSHException("Invalid key options for kdf 'none'")
            private_data = privkey_blob

        elif kdfname == "bcrypt":
            if not password:
                raise PasswordRequiredException(
                    "Private key file is encrypted")
            if cipher == 'aes256-cbc':
                mode = modes.CBC
            elif cipher == 'aes256-ctr':
                mode = modes.CTR
            else:
                raise SSHException(
                    "unknown cipher '%s' used in private key file" % cipher)

            kdf = Message(kdfoptions)
            salt = kdf.get_binary()
            rounds = kdf.get_int()

            # run bcrypt kdf to derive key and iv/nonce (desired_key_bytes = 32 + 16 bytes)
            key_iv = bcrypt.kdf(b(password),
                                salt,
                                48,
                                rounds,
                                ignore_few_rounds=True)
            key = key_iv[:32]
            iv = key_iv[32:]
            # decrypt private key blob
            decryptor = Cipher(algorithms.AES(key), mode(iv),
                               default_backend()).decryptor()
            private_data = decryptor.update(
                privkey_blob) + decryptor.finalize()

        else:
            raise SSHException(
                "unknown cipher or kdf used in private key file")

        # Unpack private key and verify checkints
        priv_msg = Message(private_data)
        checkint1 = priv_msg.get_int()
        checkint2 = priv_msg.get_int()
        if checkint1 != checkint2:
            raise SSHException(
                'OpenSSH private key file checkints do not match')

        keytype = priv_msg.get_text()
        if pub_keytype != keytype:
            raise SSHException(
                "Inconsistent key types for public and private parts")

        keydata = priv_msg.get_remainder()
        return keytype, _unpad(keydata)
Exemple #27
0
    def __init__(self, msg=None, data=None, privkey_filename=None,
                 cert_filename=None, password=None, privkey_file_obj=None,
                 cert_file_obj=None):
        self.nonce = None
        self.key = None
        self.serial = None
        self.type = None
        self.key_id = None
        self.valid_principals = None
        self.valid_after = None
        self.valid_before = None
        self.critical_options = None
        self.extensions = None
        self.reserved = None
        self.signature_key = None
        self.signature = None
        self.d = None
        self.p = None
        self.q = None

        if cert_filename is not None:
            msg = self._load_cert_from_file(cert_filename)
        elif cert_file_obj is not None:
            msg = self._load_cert(cert_file_obj)
        elif cert_filename is None and cert_file_obj is None and data is None:
            raise SSHException('Either a data object or a certificate file must be given')

        if privkey_file_obj is not None:
            self._from_private_key(privkey_file_obj, password)
        elif privkey_filename is not None:
            self._from_private_key_file(privkey_filename, password)

        if (msg is None) and (data is not None):
            msg = Message(data)

        if msg is None:
            raise SSHException('Key object may not be empty')
        if msg.get_text() != '*****@*****.**':
            raise SSHException('Invalid key')

        self.nonce = msg.get_string()

        e = msg.get_mpint()
        n = msg.get_mpint()
        # Key might've been set by a private key file. If not, set it from the
        # cert
        if self.key is None:
            self.key = rsa.RSAPublicNumbers(e=e, n=n).public_key(
                default_backend())

        self.serial = msg.get_int64()
        self.type = msg.get_int()
        self.key_id = msg.get_string()
        self.valid_principals = msg.get_string()
        self.valid_after = msg.get_int64()
        self.valid_before = msg.get_int64()
        self.critical_options = msg.get_string()
        self.extensions = msg.get_string()
        self.reserved = msg.get_string()
        self.signature_key = msg.get_string()
        self.signature = msg.get_string()
Exemple #28
0
    def _parse_service_accept(self, m):
        service = m.get_text()
        if service == "ssh-userauth":
            self._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 = b(self.password)
                m.add_string(password)
            elif self.auth_method == "publickey":
                m.add_boolean(True)
                # Use certificate contents, if available, plain pubkey
                # otherwise
                if self.private_key.public_blob:
                    m.add_string(self.private_key.public_blob.key_type)
                    m.add_string(self.private_key.public_blob.key_blob)
                else:
                    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)
                    try:
                        m.add_string(
                            sshgss.ssh_init_sec_context(
                                self.gss_host, mech, self.username
                            )
                        )
                    except GSS_EXCEPTIONS as e:
                        return self._handle_local_gss_failure(e)
                    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()
                            try:
                                next_token = sshgss.ssh_init_sec_context(
                                    self.gss_host,
                                    mech,
                                    self.username,
                                    srv_token,
                                )
                            except GSS_EXCEPTIONS as e:
                                return self._handle_local_gss_failure(e)
                            # 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: {}".format(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()
                    m.get_string()  # Lang tag - discarded
                    raise SSHException(
                        """GSS-API Error:
Major Status: {}
Minor Status: {}
Error Message: {}
""".format(
                            maj_status, min_status, err_msg
                        )
                    )
                elif ptype == MSG_USERAUTH_FAILURE:
                    self._parse_userauth_failure(m)
                    return
                else:
                    raise SSHException(
                        "Received Package: {}".format(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 "{}"'.format(self.auth_method)
                )
            self.transport._send_message(m)
        else:
            self._log(
                DEBUG, 'Service request "{}" accepted (?)'.format(service)
            )
Exemple #29
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
            # 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)
        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
            result = AUTH_SUCCESSFUL
            self.transport.server_object.check_auth_gssapi_keyex(username, result)
        else:
            result = self.transport.server_object.check_auth_none(username)
        # okay, send result
        self._send_auth_result(username, method, result)
Exemple #30
0
             result = AUTH_FAILED
 elif method == "keyboard-interactive":
     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._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._log(
             INFO,
             "Disconnect: Received an invalid GSS-API OID mechanism",
Exemple #31
0
    def _parse_service_accept(self, m):
        service = m.get_text()
        if service == "ssh-userauth":
            self._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)
                # Use certificate contents, if available, plain pubkey
                # otherwise
                if self.private_key.public_blob:
                    m.add_string(self.private_key.public_blob.key_type)
                    m.add_string(self.private_key.public_blob.key_blob)
                else:
                    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)
                    try:
                        m.add_string(
                            sshgss.ssh_init_sec_context(
                                self.gss_host, mech, self.username))
                    except GSS_EXCEPTIONS as e:
                        return self._handle_local_gss_failure(e)
                    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()
                            try:
                                next_token = sshgss.ssh_init_sec_context(
                                    self.gss_host,
                                    mech,
                                    self.username,
                                    srv_token,
                                )
                            except GSS_EXCEPTIONS as e:
                                return self._handle_local_gss_failure(e)
                            # 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: {}".format(
                            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()
                    m.get_string()  # Lang tag - discarded
                    raise SSHException("""GSS-API Error:
Major Status: {}
Minor Status: {}
Error Message: {}
""".format(maj_status, min_status, err_msg))
                elif ptype == MSG_USERAUTH_FAILURE:
                    self._parse_userauth_failure(m)
                    return
                else:
                    raise SSHException("Received Package: {}".format(
                        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 "{}"'.format(
                    self.auth_method))
            self.transport._send_message(m)
        else:
            self._log(DEBUG,
                      'Service request "{}" accepted (?)'.format(service))