def authenticate (self, service_name): session_id = self.transport.key_exchange.session_id tries = self.max_tries while tries: # this is relatively stateless, because the RFC says the client can send requests without bothering # to see if the server even wants them. # XXX allow each method to be tried, then removed from the list of # possibles? [think about combinations of service/user/etc... though] message_type, packet = self.transport.receive_message ((SSH_MSG_USERAUTH_REQUEST,)) msg, username, service, method = unpack_payload (PAYLOAD_MSG_USERAUTH_REQUEST, packet) if method == 'none': self.send_failure() elif method == 'publickey': mprobe = self.by_name.get ('publickey') if not mprobe: self.send_failure() else: (_, _, _, _, btest, alg, blob) = unpack_payload (PAYLOAD_MSG_USERAUTH_REQUEST_PK_TEST, packet) if not btest: # XXX: ask method if it's ok with serv+user+alg+blob self.send (PAYLOAD_MSG_USERAUTH_PK_OK, (SSH_MSG_USERAUTH_PK_OK, alg, blob)) else: (_, _, _, _, _, _, key, sig) = unpack_payload (PAYLOAD_MSG_USERAUTH_REQUEST_PK, packet) self.transport.debug.write ( ssh_debug.DEBUG_1, 'Trying Public Key Authentication: %r' % ((service, username, alg),)) if mprobe.authenticate (session_id, service, username, alg, key, sig): self.send (PAYLOAD_MSG_USERAUTH_SUCCESS, (SSH_MSG_USERAUTH_SUCCESS,)) return True else: tries -= 1 coro.sleep_relative (self.sleep_time) self.send_failure() elif method == 'password': mprobe = self.by_name.get ('password') if not mprobe: self.send_failure() else: (_, _, _, _, btest, password) = unpack_payload (PAYLOAD_MSG_USERAUTH_REQUEST_PASSWORD, packet) self.transport.debug.write ( ssh_debug.DEBUG_1, 'Trying Password Authentication: %r' % ((service, username,),)) if btest: self.transport.debug.write ( ssh_debug.DEBUG_1, 'Client side trying to change password: Not Yet Implemented') return send_failure() elif mprobe.authenticate (service, username, password): self.send (PAYLOAD_MSG_USERAUTH_SUCCESS, (SSH_MSG_USERAUTH_SUCCESS,)) return True else: tries -= 1 coro.sleep_relative (self.sleep_time) self.send_failure() else: tries -= 1 coro.sleep_relative (self.sleep_time) self.send_failure()
def _try_auth(self, packet, loaded_key, username, service_name): self.transport.debug.write( ssh_debug.DEBUG_1, 'Publickey Auth: Got OK for this key type.') msg, key_algorithm_name, key_blob = unpack_payload( PAYLOAD_MSG_USERAUTH_PK_OK, packet) assert (key_algorithm_name == loaded_key.name) # XXX: Check key_blob, too? # Send the actual request. # Compute signature. session_id = self.transport.key_exchange.session_id sig_data = pack_payload( PAYLOAD_USERAUTH_REQUEST_PK_SIGNATURE, (session_id, SSH_MSG_USERAUTH_REQUEST, username, service_name, 'publickey', 1, loaded_key.name, loaded_key.get_public_key_blob())) signature = loaded_key.sign(sig_data) self.transport.debug.write( ssh_debug.DEBUG_1, 'Publickey Auth: Sending userauth request.') packet = pack_payload( PAYLOAD_MSG_USERAUTH_REQUEST_PK, (SSH_MSG_USERAUTH_REQUEST, username, service_name, 'publickey', 1, loaded_key.name, loaded_key.get_public_key_blob(), signature)) self.transport.send_packet(packet) message_type, packet = self.transport.receive_message( (SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE)) if message_type == SSH_MSG_USERAUTH_SUCCESS: # Success. return elif message_type == SSH_MSG_USERAUTH_FAILURE: self.msg_userauth_failure(packet) raise Authentication_Error else: # Should never happen. raise ValueError(message_type)
def authenticate(self, authentication_method, service_name): """authenticate(self, authentication_method, service) -> None Authenticate with the remote side. <authentication_method>: <service_name>: The name of the service that you want to use after authenticating. Typically 'ssh-connection'. """ # Ask the remote side if it is OK to use this authentication service. self.debug.write(debug.DEBUG_3, 'authenticate: sending service request (%s)', (authentication_method.name,)) service_request_packet = ssh_packet.pack_payload(ssh_packet.PAYLOAD_MSG_SERVICE_REQUEST, (transport.SSH_MSG_SERVICE_REQUEST, authentication_method.name)) self.send_packet(service_request_packet) # Server will disconnect if it doesn't like our service request. self.debug.write(debug.DEBUG_3, 'authenticate: waiting for SERVICE_ACCEPT') message_type, packet = self.receive_message((transport.SSH_MSG_SERVICE_ACCEPT,)) msg, accepted_service_name = ssh_packet.unpack_payload(ssh_packet.PAYLOAD_MSG_SERVICE_ACCEPT, packet) self.debug.write(debug.DEBUG_3, 'authenticate: got SERVICE_ACCEPT') if accepted_service_name != authentication_method.name: self.send_disconnect(transport.SSH_DISCONNECT_PROTOCOL_ERROR, 'accepted service does not match requested service "%s"!="%s"' % (authentication_method.name, accepted_service_name)) # This authetnication service is OK, try to authenticate. authentication_method.authenticate(service_name)
def authenticate(self, authentication_method, service_name): """authenticate(self, authentication_method, service) -> None Authenticate with the remote side. <authentication_method>: <service_name>: The name of the service that you want to use after authenticating. Typically 'ssh-connection'. """ # Ask the remote side if it is OK to use this authentication service. self.debug.write(debug.DEBUG_3, "authenticate: sending service request (%s)", (authentication_method.name,)) service_request_packet = ssh_packet.pack_payload( ssh_packet.PAYLOAD_MSG_SERVICE_REQUEST, (transport.SSH_MSG_SERVICE_REQUEST, authentication_method.name) ) self.send_packet(service_request_packet) # Server will disconnect if it doesn't like our service request. self.debug.write(debug.DEBUG_3, "authenticate: waiting for SERVICE_ACCEPT") message_type, packet = self.receive_message((transport.SSH_MSG_SERVICE_ACCEPT,)) msg, accepted_service_name = ssh_packet.unpack_payload(ssh_packet.PAYLOAD_MSG_SERVICE_ACCEPT, packet) self.debug.write(debug.DEBUG_3, "authenticate: got SERVICE_ACCEPT") if accepted_service_name != authentication_method.name: self.send_disconnect( transport.SSH_DISCONNECT_PROTOCOL_ERROR, 'accepted service does not match requested service "%s"!="%s"' % (authentication_method.name, accepted_service_name), ) # This authetnication service is OK, try to authenticate. authentication_method.authenticate(service_name)
def msg_kexdh_init(self, packet): # mpint e msg, self.client_exchange_value = ssh_packet.unpack_payload( KEXDH_INIT_PAYLOAD, packet) # XXX make sure e is a valid number # This is y. self.server_random_value = ssh_random.get_random_number(512) # p is large safe prime (DH_PRIME) # g is a generator for a subgroup of GF(p) (DH_GENERATOR) # compute f=g**y mod p self.server_exchange_value = pow(DH_GENERATOR, self.server_random_value, DH_PRIME) self.shared_secret = pow(self.client_exchange_value, self.server_random_value, DH_PRIME) K_S = self.transport.server_key.get_public_key_blob() payload_inputs = (self.c2s_version_string, self.s2c_version_string, self.c2s_kexinit_packet, self.s2c_kexinit_packet, K_S, self.client_exchange_value, self.server_exchange_value, self.shared_secret) H = ssh_packet.pack_payload(KEXDH_HASH_PAYLOAD, payload_inputs) self.exchange_hash = hashlib.sha1(H).digest() if self.session_id is None: # The session id is the first exchange hash. self.session_id = self.exchange_hash H_sig = self.transport.server_key.sign(self.exchange_hash) packet = ssh_packet.pack_payload( KEXDH_REPLY_PAYLOAD, (SSH_MSG_KEXDH_REPLY, K_S, self.server_exchange_value, H_sig)) self.transport.send_packet(packet)
def handle_x11_request(self, want_reply, type_specific_packet_data): single_connection, auth_protocol, auth_cookie, screen_number = ssh_packet.unpack_payload( X11_CHANNEL_REQUEST_PAYLOAD, type_specific_packet_data ) # XXX fantasize about doing X11 forwarding here? I think not. if want_reply: self.send_channel_request_failure()
def handle_pty_request(self, want_reply, type_specific_packet_data): self.pty = PTY (ssh_packet.unpack_payload(PTY_CHANNEL_REQUEST_PAYLOAD, type_specific_packet_data)) if want_reply: if self.accept_pty: self.send_channel_request_success() else: self.send_channel_request_failure()
def msg_userauth_failure(self, packet): self.transport.debug.write(ssh_debug.DEBUG_1, '%s Auth: Userauth failure.', (self.name,)) msg, auths_that_can_continue, partial_success = unpack_payload(PAYLOAD_MSG_USERAUTH_FAILURE, packet) # XXX: How to handle partial_success? if self.name not in auths_that_can_continue: self.transport.debug.write(ssh_debug.DEBUG_1, '%s Auth: Not in the list of auths that can continue', (self.name,)) raise Userauth_Method_Not_Allowed_Error(auths_that_can_continue)
def msg_channel_window_adjust(self, pkt): msg, channel_id, bytes_to_add = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_WINDOW_ADJUST_PAYLOAD, pkt) channel = self.local_channels[channel_id] channel.remote_channel.window_data_left += bytes_to_add self.transport.debug.write(ssh_debug.DEBUG_3, 'channel %i window increased by %i to %i', (channel.remote_channel.channel_id, bytes_to_add, channel.remote_channel.window_data_left)) channel.window_data_added_cv.wake_all()
def msg_channel_extended_data(self, pkt): msg, channel_id, data_type_code, data = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_EXTENDED_DATA_PAYLOAD, pkt) channel = self.local_channels[channel_id] if len(data) > channel.window_data_left: self.transport.debug.write(ssh_debug.WARNING, 'channel %i %i bytes overflowed window of %i', (channel.channel_id, len(data), channel.remote_channel.window_data_left)) # Data is ignored. else: channel.window_data_left -= len(data) channel.append_extended_data_received(data_type_code, data)
def verify(self, message, signature): e, n = self.public_key rsa, blob = packet.unpack_payload(RSA_SIG_PAYLOAD, signature) if rsa != 'ssh-rsa': raise ValueError(rsa) s = number.bytes_to_long(blob) rsa_obj = RSA.construct((n, e)) modulus_n_length_in_octets = rsa_obj.size() / 8 encoded_message = self.emsa_pkcs1_v1_5_encode(message, modulus_n_length_in_octets) return rsa_obj.verify(encoded_message, (s,))
def msg_channel_data(self, pkt): msg, channel_id, data = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_DATA_PAYLOAD, pkt) channel = self.local_channels[channel_id] # XXX: In theory, we should verify that len(data) <= channel.max_packet_size if len(data) > channel.window_data_left: self.transport.debug.write(ssh_debug.WARNING, 'channel %i %i bytes overflowed window of %i', (channel.channel_id, len(data), channel.remote_channel.window_data_left)) # Data is ignored. else: channel.window_data_left -= len(data) channel.append_data_received(data)
def msg_channel_extended_data(self, pkt): msg, channel_id, data_type_code, data = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_EXTENDED_DATA_PAYLOAD, pkt) channel = self.local_channels[channel_id] if len(data) > channel.window_data_left: self.transport.debug.write(ssh_debug.WARNING, 'channel %i %i bytes overflowed window of %i', (channel.channel_id, len(data), channel.remote_channel.window_data_left)) # Data is ignored. else: channel.window_data_left -= len(data) channel.append_extended_data_received(data_type_code, data)
def msg_channel_data(self, pkt): msg, channel_id, data = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_DATA_PAYLOAD, pkt) channel = self.local_channels[channel_id] # XXX: In theory, we should verify that len(data) <= channel.max_packet_size if len(data) > channel.window_data_left: self.transport.debug.write(ssh_debug.WARNING, 'channel %i %i bytes overflowed window of %i', (channel.channel_id, len(data), channel.remote_channel.window_data_left)) # Data is ignored. else: channel.window_data_left -= len(data) channel.append_data_received(data)
def msg_channel_close(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_CLOSE_PAYLOAD, pkt) channel = self.local_channels[channel_id] del self.local_channels[channel_id] del self.remote_channels[channel.remote_channel.channel_id] # assert it is not already closed? channel.closed = 1 if not channel.remote_channel.closed: # Close the other side. channel.close() channel.set_eof()
def msg_channel_close(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_CLOSE_PAYLOAD, pkt) channel = self.local_channels[channel_id] del self.local_channels[channel_id] del self.remote_channels[channel.remote_channel.channel_id] # assert it is not already closed? channel.closed = 1 if not channel.remote_channel.closed: # Close the other side. channel.close() channel.set_eof()
def verify(self, message, signature): e, n = self.public_key rsa, blob = packet.unpack_payload(RSA_SIG_PAYLOAD, signature) if rsa != 'ssh-rsa': raise ValueError(rsa) s = number.bytes_to_long(blob) rsa_obj = RSA.construct((n, e)) modulus_n_length_in_octets = rsa_obj.size() / 8 encoded_message = self.emsa_pkcs1_v1_5_encode( message, modulus_n_length_in_octets) return rsa_obj.verify(encoded_message, (s, ))
def msg_userauth_failure(self, packet): self.transport.debug.write(ssh_debug.DEBUG_1, '%s Auth: Userauth failure.', (self.name, )) msg, auths_that_can_continue, partial_success = unpack_payload( PAYLOAD_MSG_USERAUTH_FAILURE, packet) # XXX: How to handle partial_success? if self.name not in auths_that_can_continue: self.transport.debug.write( ssh_debug.DEBUG_1, '%s Auth: Not in the list of auths that can continue', (self.name, )) raise Userauth_Method_Not_Allowed_Error(auths_that_can_continue)
def verify(self, message, signature): p, q, g, y = self.public_key dss, blob = packet.unpack_payload(DSS_SIG_PAYLOAD, signature) if dss != 'ssh-dss': raise ValueError(dss) # blob is the concatenation of r and s # r and s are 160-bit (20-byte) integers in network-byte-order assert(len(blob) == 40) r = number.bytes_to_long(blob[:20]) s = number.bytes_to_long(blob[20:]) dsa_obj = DSA.construct((y, g, p, q)) hash_of_message = hashlib.sha1(message).digest() return dsa_obj.verify(hash_of_message, (r, s))
def verify(self, message, signature): p, q, g, y = self.public_key dss, blob = packet.unpack_payload(DSS_SIG_PAYLOAD, signature) if dss != 'ssh-dss': raise ValueError, dss # blob is the concatenation of r and s # r and s are 160-bit (20-byte) integers in network-byte-order assert( len(blob) == 40 ) r = number.bytes_to_long(blob[:20]) s = number.bytes_to_long(blob[20:]) dsa_obj = DSA.construct( (y, g, p, q) ) hash_of_message = hashlib.sha1(message).digest() return dsa_obj.verify(hash_of_message, (r, s))
def msg_kexdh_reply(self, packet): # string server public host key and certificates (K_S) # mpint f # string signature of H msg, public_host_key, server_exchange_value, signature_of_h = ssh_packet.unpack_payload( KEXDH_REPLY_PAYLOAD, packet) # Create a SSH_Public_Private_Key instance from the packed string. self.server_public_host_key = parse_public_key(public_host_key) # Verify that this is a known host key. self.transport.verify_public_host_key(self.server_public_host_key) # Make sure f is a valid number if server_exchange_value <= 1 or server_exchange_value >= DH_PRIME - 1: self.transport.send_disconnect( constants.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, 'Key exchange did not succeed: Server exchange value not valid.' ) # K = f**x mod p self.shared_secret = pow(server_exchange_value, self.client_random_value, DH_PRIME) # Verify hash. # string V_C, the client's version string (CR and NL excluded) # string V_S, the server's version string (CR and NL excluded) # string I_C, the payload of the client's SSH_MSG_KEXINIT # string I_S, the payload of the server's SSH_MSG_KEXINIT # string K_S, the host key # mpint e, exchange value sent by the client # mpint f, exchange value sent by the server # mpint K, the shared secret H = ssh_packet.pack_payload( KEXDH_HASH_PAYLOAD, (self.c2s_version_string, self.s2c_version_string, self.c2s_kexinit_packet, self.s2c_kexinit_packet, public_host_key, self.client_exchange_value, server_exchange_value, self.shared_secret)) # Double check that the signature from the server matches our signature. hash = hashlib.sha1(H) self.exchange_hash = hash.digest() if self.session_id is None: # The session id is the first exchange hash. self.session_id = self.exchange_hash if not self.server_public_host_key.verify(self.exchange_hash, signature_of_h): self.transport.send_disconnect( constants.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, 'Key exchange did not succeed: Signature did not match.')
def msg_kexdh_reply(self, packet): # string server public host key and certificates (K_S) # mpint f # string signature of H msg, public_host_key, server_exchange_value, signature_of_h = ssh_packet.unpack_payload(KEXDH_REPLY_PAYLOAD, packet) W ('kexdh: KEXDH_REPLY public host blob = %r\n' % (public_host_key,)) # Create a SSH_Public_Private_Key instance from the packed string. self.server_public_host_key = parse_public_key(public_host_key) # Verify that this is a known host key. self.transport.verify_public_host_key(self.server_public_host_key) # Make sure f is a valid number if server_exchange_value <= 1 or server_exchange_value >= DH_PRIME-1: self.transport.send_disconnect(constants.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, 'Key exchange did not succeed: Server exchange value not valid.') # K = f**x mod p self.shared_secret = pow(server_exchange_value, self.client_random_value, DH_PRIME) # Verify hash. # string V_C, the client's version string (CR and NL excluded) # string V_S, the server's version string (CR and NL excluded) # string I_C, the payload of the client's SSH_MSG_KEXINIT # string I_S, the payload of the server's SSH_MSG_KEXINIT # string K_S, the host key # mpint e, exchange value sent by the client # mpint f, exchange value sent by the server # mpint K, the shared secret H = ssh_packet.pack_payload(KEXDH_HASH_PAYLOAD, (self.c2s_version_string, self.s2c_version_string, self.c2s_kexinit_packet, self.s2c_kexinit_packet, public_host_key, self.client_exchange_value, server_exchange_value, self.shared_secret)) # Double check that the signature from the server matches our signature. hash = hashlib.sha1(H) self.exchange_hash = hash.digest() if self.session_id is None: # The session id is the first exchange hash. self.session_id = self.exchange_hash if not self.server_public_host_key.verify(self.exchange_hash, signature_of_h): self.transport.send_disconnect(constants.SSH_DISCONNECT_KEY_EXCHANGE_FAILED, 'Key exchange did not succeed: Signature did not match.') # Finished... self.transport.send_newkeys()
def msg_channel_open_confirmation(self, pkt): data, offset = ssh_packet.unpack_payload_get_offset(SSH_MSG_CHANNEL_OPEN_CONFIRMATION_PAYLOAD, pkt) msg, recipient_channel, sender_channel, window_size, max_packet_size = data self.transport.debug.write(ssh_debug.DEBUG_1, 'channel %i open confirmation sender_channel=%i window_size=%i max_packet_size=%i', (recipient_channel, sender_channel, window_size, max_packet_size)) channel = self.local_channels[recipient_channel] # XXX: Assert that the channel is not already open? channel.closed = 0 channel.eof = 0 channel.remote_channel.closed = 0 channel.remote_channel.channel_id = sender_channel assert not self.remote_channels.has_key(sender_channel) self.remote_channels[sender_channel] = channel.remote_channel channel.remote_channel.window_size = window_size channel.remote_channel.window_data_left = window_size channel.remote_channel.max_packet_size = max_packet_size additional_data = ssh_packet.unpack_payload(channel.additional_packet_data_types, pkt, offset) channel.channel_open_success(additional_data)
def msg_userauth_passwd_changereq(self, packet, username, service_name): # User's password has expired. Allow the user to enter a new password. msg, prompt, language = unpack_payload( PAYLOAD_MSG_USERAUTH_PASSWD_CHANGEREQ, packet) print safe_string(prompt) old_password = self.get_password('%s\'s old password> ' % username) while 1: new_password = self.get_password('%s\'s new password> ' % username) new_password2 = self.get_password('Retype new password> ') if new_password != new_password2: print 'Passwords did not match! Try again.' else: break packet = pack_payload( PAYLOAD_MSG_USERAUTH_REQUEST_CHANGE_PASSWD, (SSH_MSG_USERAUTH_REQUEST, username, service_name, 'password', 1, old_password, new_password)) self.transport.send_packet(packet)
def _try_auth(self, packet, loaded_key, username, service_name): self.transport.debug.write(ssh_debug.DEBUG_1, 'Publickey Auth: Got OK for this key type.') msg, key_algorithm_name, key_blob = unpack_payload(PAYLOAD_MSG_USERAUTH_PK_OK, packet) assert (key_algorithm_name == loaded_key.name) # XXX: Check key_blob, too? # Send the actual request. # Compute signature. session_id = self.transport.key_exchange.session_id sig_data = pack_payload(PAYLOAD_USERAUTH_REQUEST_PK_SIGNATURE, (session_id, SSH_MSG_USERAUTH_REQUEST, username, service_name, 'publickey', 1, loaded_key.name, loaded_key.get_public_key_blob() )) signature = loaded_key.sign(sig_data) self.transport.debug.write(ssh_debug.DEBUG_1, 'Publickey Auth: Sending userauth request.') packet = pack_payload(PAYLOAD_MSG_USERAUTH_REQUEST_PK, (SSH_MSG_USERAUTH_REQUEST, username, service_name, 'publickey', 1, loaded_key.name, loaded_key.get_public_key_blob(), signature)) self.transport.send_packet(packet) message_type, packet = self.transport.receive_message((SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE)) if message_type == SSH_MSG_USERAUTH_SUCCESS: # Success. return elif message_type == SSH_MSG_USERAUTH_FAILURE: self.msg_userauth_failure(packet) raise Authentication_Error else: # Should never happen. raise ValueError(message_type)
def msg_channel_open (self, pkt): _, channel_type, remote_id, initial_window, max_packet_size = ssh_packet.unpack_payload (SSH_MSG_CHANNEL_OPEN_PAYLOAD, pkt) channel = self.new_channel_class (self) self.register_channel (channel) channel.remote_channel.closed = 0 self.transport.send ( SSH_MSG_CHANNEL_OPEN_CONFIRMATION_PAYLOAD, ( SSH_MSG_CHANNEL_OPEN_CONFIRMATION, remote_id, channel.channel_id, initial_window, max_packet_size, ) ) self.transport.debug.write ( ssh_debug.DEBUG_1, 'channel %i open confirmation sender_channel=%i window_size=%i max_packet_size=%i', ( remote_id, channel.channel_id, initial_window, max_packet_size ) )
def msg_channel_open_confirmation(self, pkt): data, offset = ssh_packet.unpack_payload_get_offset(SSH_MSG_CHANNEL_OPEN_CONFIRMATION_PAYLOAD, pkt) msg, recipient_channel, sender_channel, window_size, max_packet_size = data self.transport.debug.write( ssh_debug.DEBUG_1, 'channel %i open confirmation sender_channel=%i window_size=%i max_packet_size=%i', (recipient_channel, sender_channel, window_size, max_packet_size)) channel = self.local_channels[recipient_channel] # XXX: Assert that the channel is not already open? channel.closed = 0 channel.eof = 0 channel.remote_channel.closed = 0 channel.remote_channel.channel_id = sender_channel assert sender_channel not in self.remote_channels self.remote_channels[sender_channel] = channel.remote_channel channel.remote_channel.window_size = window_size channel.remote_channel.window_data_left = window_size channel.remote_channel.max_packet_size = max_packet_size additional_data = ssh_packet.unpack_payload(channel.additional_packet_data_types, pkt, offset) channel.channel_open_success(additional_data)
def msg_userauth_passwd_changereq(self, packet, username, service_name): # User's password has expired. Allow the user to enter a new password. msg, prompt, language = unpack_payload(PAYLOAD_MSG_USERAUTH_PASSWD_CHANGEREQ, packet) print safe_string(prompt) old_password = self.get_password('%s\'s old password> ' % username) while 1: new_password = self.get_password('%s\'s new password> ' % username) new_password2 = self.get_password('Retype new password> ') if new_password != new_password2: print 'Passwords did not match! Try again.' else: break packet = pack_payload(PAYLOAD_MSG_USERAUTH_REQUEST_CHANGE_PASSWD, (SSH_MSG_USERAUTH_REQUEST, username, service_name, 'password', 1, old_password, new_password)) self.transport.send_packet(packet)
def msg_channel_open (self, pkt): _, channel_type, remote_id, initial_window, max_packet_size = ssh_packet.unpack_payload ( SSH_MSG_CHANNEL_OPEN_PAYLOAD, pkt) channel = self.new_channel_class (self) self.register_channel (channel) channel.remote_channel.closed = 0 self.transport.send ( SSH_MSG_CHANNEL_OPEN_CONFIRMATION_PAYLOAD, ( SSH_MSG_CHANNEL_OPEN_CONFIRMATION, remote_id, channel.channel_id, initial_window, max_packet_size, ) ) self.transport.debug.write ( ssh_debug.DEBUG_1, 'channel %i open confirmation sender_channel=%i window_size=%i max_packet_size=%i', ( remote_id, channel.channel_id, initial_window, max_packet_size ) )
def msg_kexdh_init (self, packet): # mpint e msg, self.client_exchange_value = ssh_packet.unpack_payload (KEXDH_INIT_PAYLOAD, packet) # XXX make sure e is a valid number # This is y. self.server_random_value = ssh_random.get_random_number(512) # p is large safe prime (DH_PRIME) # g is a generator for a subgroup of GF(p) (DH_GENERATOR) # compute f=g**y mod p self.server_exchange_value = pow(DH_GENERATOR, self.server_random_value, DH_PRIME) self.shared_secret = pow (self.client_exchange_value, self.server_random_value, DH_PRIME) K_S = self.transport.server_key.get_public_key_blob() payload_inputs = ( self.c2s_version_string, self.s2c_version_string, self.c2s_kexinit_packet, self.s2c_kexinit_packet, K_S, self.client_exchange_value, self.server_exchange_value, self.shared_secret ) H = ssh_packet.pack_payload (KEXDH_HASH_PAYLOAD, payload_inputs) self.exchange_hash = hashlib.sha1(H).digest() if self.session_id is None: # The session id is the first exchange hash. self.session_id = self.exchange_hash H_sig = self.transport.server_key.sign (self.exchange_hash) packet = ssh_packet.pack_payload ( KEXDH_REPLY_PAYLOAD, ( SSH_MSG_KEXDH_REPLY, K_S, self.server_exchange_value, H_sig ) ) self.transport.send_packet (packet)
def set_private_key(self, private_key): rsa, n, e, d, p, q = packet.unpack_payload(RSA_PRIVATE_KEY_PAYLOAD, private_key) if rsa != 'ssh-rsa': raise ValueError(rsa) self.public_key = (n, e, d, p, q)
def msg_channel_success(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_SUCCESS_PAYLOAD, pkt) channel = self.local_channels[channel_id] channel.channel_request_success()
def msg_channel_failure(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_FAILURE_PAYLOAD, pkt) channel = self.local_channels[channel_id] channel.channel_request_failure()
def set_private_key(self, private_key): dss, p, q, g, y, x = packet.unpack_payload(DSS_PRIVATE_KEY_PAYLOAD, private_key) if dss != 'ssh-dss': raise ValueError, dss self.private_key = (p, q, g, y, x)
def msg_debug(self, packet): msg, always_display, message, language = ssh_packet.unpack_payload(ssh_packet.PAYLOAD_MSG_DEBUG, packet) self.debug.write(ssh_debug.DEBUG_1, 'SSH_MSG_DEBUG: %s', message)
def msg_kexinit(self, packet): self.remote2self.kexinit_packet = packet msg, cookie, kex_algorithms, server_host_key_algorithms, encryption_algorithms_c2s, \ encryption_algorithms_s2c, mac_algorithms_c2s, mac_algorithms_s2c, \ compression_algorithms_c2s, compression_algorithms_s2c, \ languages_c2s, languages_s2c, first_kex_packet_follows, pad = ssh_packet.unpack_payload( ssh_packet.PAYLOAD_MSG_KEXINIT, packet) self.remote2self.proactive_kex = first_kex_packet_follows self.c2s.set_supported( kex_algorithms, server_host_key_algorithms, encryption_algorithms_c2s, mac_algorithms_c2s, compression_algorithms_c2s, languages_c2s, 1) # Prefer client's list. self.s2c.set_supported( kex_algorithms, server_host_key_algorithms, encryption_algorithms_s2c, mac_algorithms_s2c, compression_algorithms_s2c, languages_s2c, 0) # Prefer client's list. # The algorithm that we use is the first item that is on the client's # list that is also on the server's list. self._matchup_kex_and_key() self._matchup('cipher') self._matchup('mac') self._matchup('compression') # XXX: lang not supported # See if we guessed the kex properly. if self.remote2self.proactive_kex and \ self.remote2self.key_exchange.name != self.key_exchange.name: # Remote side sent an incorrect initial kex packet...ignore it. self.ignore_first_packet = True if self.self2remote.proactive_kex and \ self.self2remote.key_exchange.name != self.key_exchange.name: # We sent an invalid initial kex packet. # Resend proper kex packet. self.debug.write(ssh_debug.DEBUG_1, 'msg_kexinit: Resending initial kex packet due to incorrect guess') if self.is_server: packet = self.key_exchange.get_initial_server_kex_packet() else: packet = self.key_exchange.get_initial_client_kex_packet() # packet should never be None because if proactive_kex is set, # then that means we sent the first packet. assert (packet is not None) self.send_packet(packet) # Sync up. self.remote2self.key_exchange = self.key_exchange self.self2remote.key_exchange = self.key_exchange self.remote2self.server_key = self.server_key self.self2remote.server_key = self.server_key # Make sure kex algorithm has the information it needs. self.key_exchange.set_info(self.c2s.version_string, self.s2c.version_string, self.c2s.kexinit_packet, self.s2c.kexinit_packet, self.s2c.supported_server_keys)
def msg_disconnect(self, packet): msg, reason_code, description, language = ssh_packet.unpack_payload( ssh_packet.PAYLOAD_MSG_DISCONNECT, packet) self.disconnect() raise SSH_Protocol_Error(reason_code, description)
def msg_debug(self, packet): msg, always_display, message, language = ssh_packet.unpack_payload( ssh_packet.PAYLOAD_MSG_DEBUG, packet) self.debug.write(ssh_debug.DEBUG_1, 'SSH_MSG_DEBUG: %s', message)
def msg_channel_success(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_SUCCESS_PAYLOAD, pkt) channel = self.local_channels[channel_id] channel.channel_request_success()
def set_private_key(self, private_key): rsa, n, e, d, p, q = packet.unpack_payload(RSA_PRIVATE_KEY_PAYLOAD, private_key) if rsa != 'ssh-rsa': raise ValueError(rsa) self.public_key = (n, e, d, p, q)
def msg_channel_open_failure(self, pkt): msg, channel_id, reason_code, reason_text, language = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_OPEN_FAILURE_PAYLOAD, pkt) channel = self.local_channels[channel_id] # XXX: Assert that the channel is not already open? channel.channel_open_failure(reason_code, reason_text, language)
def msg_channel_eof(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_EOF_PAYLOAD, pkt) channel = self.local_channels[channel_id] # assert it is not already closed? channel.set_eof()
def msg_userauth_banner(self, packet): msg, message, language = unpack_payload(PAYLOAD_MSG_USERAUTH_BANNER, packet) print safe_string(message)
def set_private_key(self, private_key): dss, p, q, g, y, x = packet.unpack_payload(DSS_PRIVATE_KEY_PAYLOAD, private_key) if dss != 'ssh-dss': raise ValueError(dss) self.private_key = (p, q, g, y, x)
def msg_unimplemented(self, packet): msg, seq_number = ssh_packet.unpack_payload( ssh_packet.PAYLOAD_MSG_UNIMPLEMENTED, packet) self.debug.write(ssh_debug.DEBUG_1, 'SSH_MSG_UNIMPLEMENTED: %i', seq_number)
def msg_disconnect(self, packet): msg, reason_code, description, language = ssh_packet.unpack_payload (ssh_packet.PAYLOAD_MSG_DISCONNECT, packet) self.disconnect() raise SSH_Protocol_Error, (reason_code, description)
def msg_kexinit(self, packet): self.remote2self.kexinit_packet = packet msg, cookie, kex_algorithms, server_host_key_algorithms, encryption_algorithms_c2s, \ encryption_algorithms_s2c, mac_algorithms_c2s, mac_algorithms_s2c, \ compression_algorithms_c2s, compression_algorithms_s2c, \ languages_c2s, languages_s2c, first_kex_packet_follows, pad = ssh_packet.unpack_payload( ssh_packet.PAYLOAD_MSG_KEXINIT, packet) self.remote2self.proactive_kex = first_kex_packet_follows self.c2s.set_supported(kex_algorithms, server_host_key_algorithms, encryption_algorithms_c2s, mac_algorithms_c2s, compression_algorithms_c2s, languages_c2s, 1) # Prefer client's list. self.s2c.set_supported(kex_algorithms, server_host_key_algorithms, encryption_algorithms_s2c, mac_algorithms_s2c, compression_algorithms_s2c, languages_s2c, 0) # Prefer client's list. # The algorithm that we use is the first item that is on the client's # list that is also on the server's list. self._matchup_kex_and_key() self._matchup('cipher') self._matchup('mac') self._matchup('compression') # XXX: lang not supported # See if we guessed the kex properly. if self.remote2self.proactive_kex and \ self.remote2self.key_exchange.name != self.key_exchange.name: # Remote side sent an incorrect initial kex packet...ignore it. self.ignore_first_packet = True if self.self2remote.proactive_kex and \ self.self2remote.key_exchange.name != self.key_exchange.name: # We sent an invalid initial kex packet. # Resend proper kex packet. self.debug.write( ssh_debug.DEBUG_1, 'msg_kexinit: Resending initial kex packet due to incorrect guess' ) if self.is_server: packet = self.key_exchange.get_initial_server_kex_packet() else: packet = self.key_exchange.get_initial_client_kex_packet() # packet should never be None because if proactive_kex is set, # then that means we sent the first packet. assert (packet is not None) self.send_packet(packet) # Sync up. self.remote2self.key_exchange = self.key_exchange self.self2remote.key_exchange = self.key_exchange self.remote2self.server_key = self.server_key self.self2remote.server_key = self.server_key # Make sure kex algorithm has the information it needs. self.key_exchange.set_info(self.c2s.version_string, self.s2c.version_string, self.c2s.kexinit_packet, self.s2c.kexinit_packet, self.s2c.supported_server_keys)
def msg_unimplemented(self, packet): msg, seq_number = ssh_packet.unpack_payload(ssh_packet.PAYLOAD_MSG_UNIMPLEMENTED, packet) self.debug.write(ssh_debug.DEBUG_1, 'SSH_MSG_UNIMPLEMENTED: %i', seq_number)
def msg_userauth_banner(self, packet): msg, message, language = unpack_payload(PAYLOAD_MSG_USERAUTH_BANNER, packet) print safe_string(message)
def set_public_key(self, public_key): dss, p, q, g, y = packet.unpack_payload(DSS_PUBLIC_KEY_PAYLOAD, public_key) if dss != 'ssh-dss': raise ValueError(dss) self.public_key = (p, q, g, y)
def msg_channel_open_failure(self, pkt): msg, channel_id, reason_code, reason_text, language = ssh_packet.unpack_payload( SSH_MSG_CHANNEL_OPEN_FAILURE_PAYLOAD, pkt) channel = self.local_channels[channel_id] # XXX: Assert that the channel is not already open? channel.channel_open_failure(reason_code, reason_text, language)
def set_public_key(self, public_key): dss, p, q, g, y = packet.unpack_payload(DSS_PUBLIC_KEY_PAYLOAD, public_key) if dss != 'ssh-dss': raise ValueError, dss self.public_key = (p, q, g, y)
def set_public_key(self, public_key): rsa, e, n = packet.unpack_payload(RSA_PUBLIC_KEY_PAYLOAD, public_key) if rsa != 'ssh-rsa': raise ValueError(rsa) self.public_key = (e, n)
def authenticate(self, service_name): session_id = self.transport.key_exchange.session_id tries = self.max_tries while tries: # this is relatively stateless, because the RFC says the client can send requests without bothering # to see if the server even wants them. # XXX allow each method to be tried, then removed from the list of # possibles? [think about combinations of service/user/etc... though] message_type, packet = self.transport.receive_message( (SSH_MSG_USERAUTH_REQUEST, )) msg, username, service, method = unpack_payload( PAYLOAD_MSG_USERAUTH_REQUEST, packet) if method == 'none': self.send_failure() elif method == 'publickey': mprobe = self.by_name.get('publickey') if not mprobe: self.send_failure() else: (_, _, _, _, btest, alg, blob) = unpack_payload( PAYLOAD_MSG_USERAUTH_REQUEST_PK_TEST, packet) if not btest: # XXX: ask method if it's ok with serv+user+alg+blob self.send(PAYLOAD_MSG_USERAUTH_PK_OK, (SSH_MSG_USERAUTH_PK_OK, alg, blob)) else: (_, _, _, _, _, _, key, sig) = unpack_payload(PAYLOAD_MSG_USERAUTH_REQUEST_PK, packet) self.transport.debug.write( ssh_debug.DEBUG_1, 'Trying Public Key Authentication: %r' % ((service, username, alg), )) if mprobe.authenticate(session_id, service, username, alg, key, sig): self.send(PAYLOAD_MSG_USERAUTH_SUCCESS, (SSH_MSG_USERAUTH_SUCCESS, )) return True else: tries -= 1 coro.sleep_relative(self.sleep_time) self.send_failure() elif method == 'password': mprobe = self.by_name.get('password') if not mprobe: self.send_failure() else: (_, _, _, _, btest, password) = unpack_payload( PAYLOAD_MSG_USERAUTH_REQUEST_PASSWORD, packet) self.transport.debug.write( ssh_debug.DEBUG_1, 'Trying Password Authentication: %r' % (( service, username, ), )) if btest: self.transport.debug.write( ssh_debug.DEBUG_1, 'Client side trying to change password: Not Yet Implemented' ) return send_failure() elif mprobe.authenticate(service, username, password): self.send(PAYLOAD_MSG_USERAUTH_SUCCESS, (SSH_MSG_USERAUTH_SUCCESS, )) return True else: tries -= 1 coro.sleep_relative(self.sleep_time) self.send_failure() else: tries -= 1 coro.sleep_relative(self.sleep_time) self.send_failure()
def set_public_key(self, public_key): rsa, e, n = packet.unpack_payload(RSA_PUBLIC_KEY_PAYLOAD, public_key) if rsa != 'ssh-rsa': raise ValueError(rsa) self.public_key = (e, n)
def _connect(self, transport, authenticator): # transport is already connected # Send identification string. self.transport = transport if self.s2c.comments: comments = ' ' + self.s2c.comments else: comments = '' self.s2c.version_string = 'SSH-' + self.s2c.protocol_version + '-' + self.s2c.software_version + comments transport.write(self.s2c.version_string + '\r\n') # Receive client's identification string. while 1: line = transport.read_line() if line.startswith('SSH-'): # Got the identification string. self.c2s.version_string = line # See if there are any optional comments. i = line.find(' ') if i != -1: self.c2s.comments = line[i + 1:] line = line[:i] # Break up the identification string into its parts. parts = line.split('-') if len(parts) != 3: self.send_disconnect( ssh_transport.SSH_DISCONNECT_PROTOCOL_ERROR, 'server identification invalid: %r' % line) self.c2s.protocol_version = parts[1] self.c2s.software_version = parts[2] if self.c2s.protocol_version not in ('1.99', '2.0'): self.send_disconnect( ssh_transport. SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, 'protocol version not supported: %r' % self.c2s.protocol_version) break self.send_kexinit() self.s2c.set_preferred('key_exchange') self.s2c.set_preferred('server_key') if self.self2remote.proactive_kex: # Go ahead and send our kex packet with our preferred algorithm. # This will assume the client side supports the algorithm. self.s2c.set_preferred('key_exchange') self.s2c.set_preferred('server_key') self.set_key_exchange() self.debug.write( debug.DEBUG_3, 'key exchange: sending proactive server kex packet') packet = self.c2s.key_exchange.get_initial_server_kex_packet() self.send_packet(packet) self.start_receive_thread() # Receive server kexinit self._process_kexinit() self.debug.write(debug.DEBUG_3, 'key exchange: got kexinit') self.debug.write( debug.DEBUG_3, 'key exchange: self.server_key=%r' % (self.server_key, )) if not self.self2remote.proactive_kex: self.debug.write( debug.DEBUG_3, 'key exchange: sending initial server kex packet') packet = self.key_exchange.get_initial_server_kex_packet() if packet: # It is possible for a key exchange algorithm to not have # an initial packet to send on the client side. self.send_packet(packet) message_type, packet = self.receive_message( (SSH_MSG_SERVICE_REQUEST, )) msg, service_name = ssh_packet.unpack_payload( ssh_packet.PAYLOAD_MSG_SERVICE_REQUEST, packet) self.debug.write(debug.DEBUG_1, 'service_request: %r' % (service_name, )) # XXX consider other possibilities if service_name != 'ssh-userauth': self.send_disconnect(SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, "not today, zurg") else: self.send_packet( ssh_packet.pack_payload( ssh_packet.PAYLOAD_MSG_SERVICE_ACCEPT, ( ssh_transport.SSH_MSG_SERVICE_ACCEPT, 'ssh-userauth', ))) authenticator.authenticate(service_name)
def get_key_algorithm(self, key): name = ssh_packet.unpack_payload((ssh_packet.STRING,), key)[0] for key_alg in self.supported_server_keys: if key_alg.name == name: return key_alg raise ValueError(name)
def msg_channel_failure(self, pkt): msg, channel_id = ssh_packet.unpack_payload(SSH_MSG_CHANNEL_FAILURE_PAYLOAD, pkt) channel = self.local_channels[channel_id] channel.channel_request_failure()
def _connect(self, transport, authenticator): # transport is already connected # Send identification string. self.transport = transport if self.s2c.comments: comments = ' ' + self.s2c.comments else: comments = '' self.s2c.version_string = 'SSH-' + self.s2c.protocol_version + '-' + self.s2c.software_version + comments transport.write(self.s2c.version_string + '\r\n') # Receive client's identification string. while 1: line = transport.read_line() if line.startswith('SSH-'): # Got the identification string. self.c2s.version_string = line # See if there are any optional comments. i = line.find(' ') if i!=-1: self.c2s.comments = line[i+1:] line = line[:i] # Break up the identification string into its parts. parts = line.split('-') if len(parts) != 3: self.send_disconnect ( ssh_transport.SSH_DISCONNECT_PROTOCOL_ERROR, 'server identification invalid: %r' % line ) self.c2s.protocol_version = parts[1] self.c2s.software_version = parts[2] if self.c2s.protocol_version not in ('1.99', '2.0'): self.send_disconnect ( ssh_transport.SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED, 'protocol version not supported: %r' % self.c2s.protocol_version ) break self.send_kexinit() self.s2c.set_preferred('key_exchange') self.s2c.set_preferred('server_key') if self.self2remote.proactive_kex: # Go ahead and send our kex packet with our preferred algorithm. # This will assume the client side supports the algorithm. self.s2c.set_preferred('key_exchange') self.s2c.set_preferred('server_key') self.set_key_exchange() self.debug.write(debug.DEBUG_3, 'key exchange: sending proactive server kex packet') packet = self.c2s.key_exchange.get_initial_server_kex_packet() self.send_packet(packet) self.start_receive_thread() # Receive server kexinit self._process_kexinit() self.debug.write(debug.DEBUG_3, 'key exchange: got kexinit') self.debug.write(debug.DEBUG_3, 'key exchange: self.server_key=%r' % (self.server_key,)) if not self.self2remote.proactive_kex: self.debug.write(debug.DEBUG_3, 'key exchange: sending initial server kex packet') packet = self.key_exchange.get_initial_server_kex_packet() if packet: # It is possible for a key exchange algorithm to not have # an initial packet to send on the client side. self.send_packet(packet) message_type, packet = self.receive_message ((SSH_MSG_SERVICE_REQUEST,)) msg, service_name = ssh_packet.unpack_payload (ssh_packet.PAYLOAD_MSG_SERVICE_REQUEST, packet) self.debug.write (debug.DEBUG_1, 'service_request: %r' % (service_name,)) # XXX consider other possibilities if service_name != 'ssh-userauth': self.send_disconnect (SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, "not today, zurg") else: self.send_packet ( ssh_packet.pack_payload ( ssh_packet.PAYLOAD_MSG_SERVICE_ACCEPT, ( ssh_transport.SSH_MSG_SERVICE_ACCEPT, 'ssh-userauth', ) ) ) authenticator.authenticate (service_name)