//---------------------------------------------------------------------------- // // Copyright (C) Intel Corporation, 2006 - 2007. // // File: TunnelSupplier.cpp // // Contents: Handles incoming data on APF tunnel. // // Notes: //---------------------------------------------------------------------------- #include #include #include "TunnelSupplier.h" #include "TunnelHandler.h" #include "TunnelConsumer.h" #include "TunnelManager.h" #include "TcpConsumer.h" #include "ChannelConsumer.h" #include "APF.h" #include "connector.h" #include "global.h" #include "DevicePresence.h" #define TEXT_UUID_SIZE 37 #define DELIMITER_HOST_PORT ":" #define PORT_STR_LENGTH 5 // Utility function declaration void StringFromUUID(const unsigned char binUUID[16], unsigned char textUUID[TEXT_UUID_SIZE] ); STATUS AMT_Tunnel_Supplier::processReturnVal(STATUS rep) { switch (rep) { case STATUS_SUCCESS: // Success case STATUS_RECOVER_ERROR: // some error we wish to keep this tunnel alive case STATUS_NETWORK_ERROR: // We can't send disconnect in this case case STATUS_CONNECTION_CLOSED: // We received disconnect message no need to reply return rep; case STATUS_AUTH_FAILURE: tunnel_handler_->_disconnect_data.setParams(APF_DISCONNECT_ILLEGAL_USER_NAME, true, PRIO_LOW); return STATUS_CONNECTION_CLOSED; } // All other status return from any process flow, is invalid and we are closing the channel tunnel_handler_->_disconnect_data.setParams(true, PRIO_HIGH); return STATUS_CONNECTION_CLOSED; } AMT_Tunnel_Supplier::AMT_Tunnel_Supplier(AMT_Tunnel_Handler *tunnel_handler): tunnel_handler_(tunnel_handler), major_version_(), minor_version_(), trigger_reason_() { } AMT_Tunnel_Supplier::~AMT_Tunnel_Supplier(void) { ACE_DEBUG((MY_DEBUG ACE_TEXT("--->AMT_Tunnel_Supplier dtor\n"))); } // Get reference to this tunnel socket stream ACE_SOCK_Stream& AMT_Tunnel_Supplier::getPeer(void) const { return (ACE_SOCK_Stream &)tunnel_handler_->peer(); } // Get reference to this tunnel consumer AMT_Tunnel_Consumer* AMT_Tunnel_Supplier::consumer() const { return tunnel_handler_->consumer(); } // Get reference to this tunnel channel manager Channel_Manager& AMT_Tunnel_Supplier::channel_manager() const { return tunnel_handler_->channel_mgr(); } void AMT_Tunnel_Supplier::setDisconnectReason(ACE_UINT32 disconnect_reason) { tunnel_handler_->_disconnect_data._disconnect_reason = disconnect_reason; } ACE_UINT32 AMT_Tunnel_Supplier::getDisconnectReason() { return tunnel_handler_->_disconnect_data._disconnect_reason; } // Set/Get this tunnel state void AMT_Tunnel_Supplier::tunnelState (int state) { tunnel_handler_->tunnelState(state); } int AMT_Tunnel_Supplier::tunnelState (void) const { return tunnel_handler_->tunnelState(); } // Receive and process incoming APF message. STATUS AMT_Tunnel_Supplier::handle_input() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::handle_input")); int res; ACE_UINT8 messageType = 0; STATUS status; // Get message Type if ((res = getPeer().recv_n(&messageType,1, &ACE_Time_Value(*getMaxTunnelTimeout()))) <=0) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier failed reading APF message type from network\n\tres= %d \terrno= %d\n"), res, errno)); return STATUS_NETWORK_ERROR; } switch (messageType) { case APF_DISCONNECT: status = process_disconnect(); break; case APF_SERVICE_REQUEST: status = process_service_request(); break; case APF_USERAUTH_REQUEST: status = process_userauth_request(); break; case APF_GLOBAL_REQUEST: status = process_global_message(); break; case APF_CHANNEL_OPEN: status = process_channel_open_direct(); break; case APF_CHANNEL_OPEN_CONFIRMATION: case APF_CHANNEL_OPEN_FAILURE: status = process_channel_open_reply(messageType); break; case APF_CHANNEL_WINDOW_ADJUST: status = process_channel_window_adjust(); break; case APF_CHANNEL_DATA: status = process_channel_data(); break; case APF_CHANNEL_CLOSE: status = process_channel_close(); break; case APF_PROTOCOLVERSION: status = process_init(); break; case APF_KEEPALIVE_REQUEST: status = process_KeepAliveReq(); break; case APF_KEEPALIVE_OPTIONS_REQ: status = process_KeepAliveOptionReq(); break; // This tunnel can't receive any of the following message types: case APF_SERVICE_ACCEPT: case APF_USERAUTH_FAILURE: case APF_USERAUTH_SUCCESS: case APF_REQUEST_SUCCESS: case APF_REQUEST_FAILURE: default: ACE_DEBUG((MY_DEBUG ACE_TEXT("[%s] AMT_Tunnel_Supplier received illegal message type (%d) disconnecting\n"),identifier(), messageType)); status = STATUS_ILLEGAL_APF_MESSAGE; setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); break; } return processReturnVal(status); } // negotiate protocol version STATUS AMT_Tunnel_Supplier::process_init() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_init")); STATUS rep = STATUS_SUCCESS; // Check tunnel state if (tunnelState() != AMT_Tunnel_Handler::SESSION_INIT) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received protocol version in invalid state (%d)\n"), tunnelState())); return STATUS_INVALID_TUNNEL_STATE; } APF_ProtocolVersion his_version; // First read AMT version message if ((rep = his_version.read(getPeer())) != STATUS_SUCCESS){ ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed reading AMT version message\n"))); return rep; } // Validate the version message format. if (his_version.triggerReason < USER_INITIATED_REQUEST || his_version.triggerReason > PERIODIC_REQUEST) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received invalid version message\n"))); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return STATUS_ILLEGAL_APF_MESSAGE; } // Save trigger reason and UUID trigger_reason_ = his_version.triggerReason; for (int i=0; i < 16; i++) UUID_[i] = his_version.systemId[i]; // Send version reply to AMT APF_ProtocolVersion my_version; my_version.majorVersion = (ACE_UINT32)APF_PROTOCOL_VERSION_MAJOR; my_version.minorVersion = (ACE_UINT32)APF_PROTOCOL_VERSION_MINOR; // for debug my_version.triggerReason = 0; for (int i=0; i < 16; i++) { my_version.systemId[i] = (ACE_UINT8)i; my_version.reserved[i] = (ACE_UINT32)i; } if ((rep = consumer()->send_APF_message(my_version)) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to send version message\n"))); return rep; } // Decide the protocol version as the smallest major_version_ = my_version.majorVersion; minor_version_ = my_version.minorVersion; // This implementation only supports one protocol version // If AMTs protocol is lower reject this connection if ((his_version.majorVersion < my_version.majorVersion) || ((his_version.majorVersion == my_version.majorVersion && his_version.minorVersion < my_version.minorVersion))) { ACE_DEBUG((MY_WARNING ACE_TEXT("[%s] Unsupported APF version (%d.%d)\n"),identifier(), his_version.majorVersion, his_version.minorVersion)); setDisconnectReason(APF_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED); return STATUS_ILLEGAL_APF_MESSAGE; } // once agreed upon, we completely ignore the protocol version for now. tunnelState(AMT_Tunnel_Handler::SESSION_OPENED); unsigned char textUUID[TEXT_UUID_SIZE]; StringFromUUID(UUID_, textUUID); tunnel_handler_->set_identifier(ACE_CString((char*)textUUID,TEXT_UUID_SIZE)); ACE_DEBUG ((MY_INFO ACE_TEXT ("Intel remote client %s is now connected.\n"), identifier())); return STATUS_SUCCESS; } // handle disconnect message STATUS AMT_Tunnel_Supplier::process_disconnect() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_disconnect")); STATUS rep; APF_Disconnect msg; if ((rep = msg.read(getPeer())) != STATUS_SUCCESS){ ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed reading disconnect message\n"))); return STATUS_NETWORK_ERROR; } ACE_DEBUG ((MY_DEBUG ACE_TEXT ("Tunnel disconnected (reason code:%d)\n"), msg.reason)); //close socket for write: getPeer().close_writer(); //set disconnect params - so later on, on handle_close, we will inform consumer to close itself. tunnel_handler_->_disconnect_data.setParams(false, PRIO_HIGH); // Remove this supplier from the reactor return STATUS_CONNECTION_CLOSED; } // handle service request STATUS AMT_Tunnel_Supplier::process_service_request() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_service_request")); STATUS rep = STATUS_SUCCESS; APF_ServiceRequest request; if ((rep = request.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed reading service request message\n"))); return rep; } ACE_DEBUG ((MY_DEBUG ACE_TEXT ("Service request: %s\n"), request.serviceName.c_str())); // Handle auth SERVICE request if (request.serviceName.compare(APF_SERVICE_AUTH) == 0) { if (tunnelState() != AMT_Tunnel_Handler::SESSION_OPENED) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received APF auth service request in invalid state (%d)\n"), tunnelState())); return STATUS_AUTH_FAILURE; } tunnelState(AMT_Tunnel_Handler::AUTH_PENDING); APF_ServiceAccept accept; accept.serviceName = request.serviceName; accept.serviceNameLength = request.serviceNameLength; rep = consumer()->send_APF_message(accept); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send reply for APF_SERVICE_AUTH request")); return STATUS_SUCCESS; } // Handle port forwarding service request if (request.serviceName.compare(APF_SERVICE_PFWD) == 0) { // We allow port forwarding without authentication for now // Here we should check whether an authentication is required const bool* amtNeedAuthentication = getAmtNeedAuthentication(); if (amtNeedAuthentication == NULL) { ACE_DEBUG((MY_DEBUG ACE_TEXT("Failed to get if AMT authentication is required."))); return STATUS_FAILURE; } if ((*amtNeedAuthentication) && (tunnelState() != AMT_Tunnel_Handler::SESSION_AUTHENTICATED)) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received APF pfwd service request in invalid state (%d)\n"), tunnelState())); tunnel_handler_->_disconnect_data.setParams(APF_DISCONNECT_ILLEGAL_USER_NAME, true, PRIO_LOW); return STATUS_INVALID_TUNNEL_STATE; } tunnelState(AMT_Tunnel_Handler::PFWD_OPEN); APF_ServiceAccept accept; accept.serviceName = request.serviceName; accept.serviceNameLength = request.serviceNameLength; rep = consumer()->send_APF_message(accept); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send reply for APF_SERVICE_PFWD request")); return STATUS_SUCCESS; } ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received APF service request with illegal service name (%s)\n"), request.serviceName.c_str())); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return STATUS_ILLEGAL_APF_MESSAGE; } // handle user authentication message STATUS AMT_Tunnel_Supplier::process_userauth_request() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_userauth_request")); STATUS rep = STATUS_SUCCESS; // Check tunnel state if (tunnelState() != AMT_Tunnel_Handler::AUTH_PENDING) { ACE_DEBUG((MY_DEBUG ACE_TEXT("received user authentication request in invalid state (%d)\n"), tunnelState())); return STATUS_INVALID_TUNNEL_STATE; } APF_UserauthRequest request; if ((rep = request.read(getPeer())) != STATUS_SUCCESS){ ACE_DEBUG((MY_DEBUG ACE_TEXT("failed to read user authentication request\n"))); return rep; } // Authentication parameters seem to be fine. // Rest of auth depends on dll and parameters, in case of auth failure, a disconnect is issued anyway. // ==> Can cancel the timeout on tunnel opening . ACE_DEBUG((MY_DEBUG ACE_TEXT("Got user authentication request, canceling timeout.\n"))); tunnel_handler_->reactor()->cancel_timer(tunnel_handler_); ACE_DEBUG ((MY_DEBUG ACE_TEXT ("User authentication request\n\tUsername:\t%s\n"), request.username.c_str())); // Check method name if (request.methodName.compare(APF_USERAUTH_PASSWORD) == 0) { //verify the user authorized: // Not currently supported in Linux #ifdef ACE_WIN32 char error[MAX_DLL_ERR_LEN]; const bool* amtNeedAuthentication = getAmtNeedAuthentication(); const ACE_TString* amtDllName = getAmtDllName(); const ACE_TString* amtDllParameters = getAmtDllParameters(); if (amtNeedAuthentication == NULL || amtDllName == NULL || amtDllParameters == NULL) { ACE_DEBUG((MY_INFO ACE_TEXT("Failed to get AMT authentication details.\n"), identifier(), error)); return STATUS_AUTH_FAILURE; } Authentication_Param amt_auth_params(*amtNeedAuthentication, *amtDllName, *amtDllParameters); if ((amt_auth_params._authenticate)&& (_amt_auth_func != NULL) && (_amt_auth_func (request.username.c_str(), request.password.c_str(), amt_auth_params._dllParams.c_str(), &(error[0]), MAX_DLL_ERR_LEN ) == 0)) { ACE_DEBUG((MY_INFO ACE_TEXT("[%s] Failed to authenticate Intel remote client: %s\n"), identifier(), error)); #if defined (ACE_WIN32) && !defined (ACE_LACKS_WIN32_SERVICES) && defined (_SERVICE) ACE_CString errStr(error); if (errStr.find("Parse connection string error") != ACE_CString::npos) { m_pLogger.LogErrorEvent(MPS_ERROR, MPS_AUTH_BAD_USAGE); } else if (errStr.find("Error opening file.") != ACE_CString::npos) { m_pLogger.LogErrorEvent(MPS_ERROR, MPS_AUTH_BAD_FILE); } else if (errStr.find("SOAP failure") != ACE_CString::npos) { m_pLogger.LogErrorEvent(MPS_ERROR, MPS_AUTH_SOAP_FAILURE); } #endif /* ACE_WIN32 && !(ACE_LACKS_WIN32_SERVICES) && (_SERVICE)*/ //prepare reply message: APF_UserauthFailure auth_failure; auth_failure.methodNameListLength = (ACE_UINT32)ACE_OS::strlen(APF_USERAUTH_FAILURE_STR); auth_failure.methodNameList = APF_USERAUTH_FAILURE_STR; //send the msg: rep = consumer()->send_APF_message(auth_failure,PRIO_HIGH); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send reply for APF_USERAUTH_FAILURE request\n")); return STATUS_AUTH_FAILURE; } #endif /* ACE_WIN32 */ ACE_DEBUG((MY_DEBUG ACE_TEXT("authentication SUCCESS\n"))); } else if(request.methodName.compare(APF_USERAUTH_NONE) == 0) { const bool* amtNeedAuthentication = getAmtNeedAuthentication(); if (amtNeedAuthentication == NULL) { ACE_DEBUG((MY_INFO ACE_TEXT("Failed to get if AMT authentication is required."))); return STATUS_AUTH_FAILURE; } if(*amtNeedAuthentication) { ACE_DEBUG((MY_INFO ACE_TEXT("[%s] Failed to authenticate Intel remote client, no authentication provided\n"), identifier())); //prepare reply message: APF_UserauthFailure auth_failure; auth_failure.methodNameListLength = (ACE_UINT32)ACE_OS::strlen(APF_USERAUTH_FAILURE_STR); auth_failure.methodNameList = APF_USERAUTH_FAILURE_STR; //send the msg: rep = consumer()->send_APF_message(auth_failure,PRIO_HIGH); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send reply for APF_USERAUTH_FAILURE request\n")); return STATUS_AUTH_FAILURE; } else { // Password is not needed - let him in. ACE_DEBUG ((MY_DEBUG ACE_TEXT ("\tNo password required\n"))); } } else { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: invalid method name (%s) in user authentication request\n"), request.methodName.c_str())); return STATUS_ILLEGAL_APF_MESSAGE; } tunnelState(AMT_Tunnel_Handler::SESSION_AUTHENTICATED); // For now we always accept the user rep = consumer()->send_APF_message((ACE_UINT8)APF_USERAUTH_SUCCESS); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send reply for APF_USERAUTH_SUCCESS request\n")); return STATUS_SUCCESS; } // read a global message from the socket and process it. STATUS AMT_Tunnel_Supplier::process_global_message() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_global_message\n")); STATUS rep = STATUS_SUCCESS; APF_GlobalRequestHeader header; if ((rep = header.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read global message\n"))); return rep; } // No need for PFWD in UDP if (header.requestString.compare(APF_UDP_SENDTO) == 0) { return process_udp_send_to(); } if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received global message in invalid state\n"))); return STATUS_INVALID_TUNNEL_STATE; } if (header.requestString.compare(APF_TCP_FORWARD_REQUEST) == 0) { return process_tcp_forward(); } if (header.requestString.compare(APF_TCP_FORWARD_CANCEL) == 0) { return process_tcp_forward_cancel(); } ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: invalid global message string\n\t%s\n"), header.requestString.c_str())); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return STATUS_ILLEGAL_APF_MESSAGE; } // read a tcp forward request from the socket and process it. STATUS AMT_Tunnel_Supplier::process_tcp_forward() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_tcp_forward\n")); APF_TcpForwardRequest request; APF_TcpForwardReply reply; STATUS rep; if ((rep = request.read(getPeer())) != STATUS_SUCCESS){ ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read tcp forward request\n"))); return rep; } //removes '[' ']' if exists (legal in ipv6) // ACE_CString addr_to_bind = request.addressToBind; if (request.addressToBind.find("[") != ACE_CString::npos) { request.addressToBind = request.addressToBind.substr(request.addressToBind.find('[')+1, request.addressToBind.find(']')-1); request.addressToBindLength = request.addressToBind.length(); } { ACE_WRITE_GUARD_RETURN( ACE_RW_Thread_Mutex, lock, Tunnel_Manager::instance().guard(), STATUS_FAILURE); // Check if this address and port already exists in the tunnel manager. AMT_Tunnel_Handler* old_tunnel = NULL; if ((old_tunnel = Tunnel_Manager::instance().find_tunnel(request.addressToBind, request.port)) != NULL) { if (old_tunnel == tunnel_handler_) { // Tunnel already exists rep = consumer()->send_APF_message((ACE_UINT8)APF_REQUEST_FAILURE); CHECK_STATUS_REP(rep, ACE_TEXT("AMT_Tunnel_Supplier: failed to send tcp forward request failure\n")); return STATUS_SUCCESS; } //if such tunnel exist - close the old one. ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received tcp forward request on existed address and port\n\t%s:%d\n"), request.addressToBind.c_str(), request.port)); //remove from tunnel mangers map: tunnel_handler_->removeFromTunnelMgr(); lock.release(); tunnel_handler_->reactor()->remove_handler(old_tunnel, ACE_Event_Handler::READ_MASK); } if (request.port == 0) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received tcp forward request with illegal port = 0\n"))); rep = consumer()->send_APF_message((ACE_UINT8)APF_REQUEST_FAILURE); CHECK_STATUS_REP(rep, ACE_TEXT("AMT_Tunnel_Supplier: failed to send tcp forward request failure\n")); return STATUS_SUCCESS; } (tunnel_handler_)->set_identifier(request.addressToBind); ACE_DEBUG ((MY_INFO ACE_TEXT ("Intel remote client %s started port forwarding to address %s:%d\n"), identifier(), request.addressToBind.c_str(), request.port)); //save this connection details: (tunnel_handler_->_tcp_forward_connections).push_back(Port_Address(request.addressToBind, request.port)); int tmp = 0; if ((tmp = Tunnel_Manager::instance().add_tunnel(tunnel_handler_, request.addressToBind, request.port)) != 0) { reply.status = APF_REQUEST_FAILURE; } else { // Send the reply back to AMT reply.status = APF_REQUEST_SUCCESS; reply.portBound = request.port; } } if (sendAlertToSubscribMCList( mps__ConnectionStateTypeDefinition__CONNECTED, request.addressToBind, request.port) == STATUS_FAILURE) { ACE_ERROR_RETURN ((MY_WARNING ACE_TEXT("[%s] Failed to send SOAP notification upon new port forwarding\n"),identifier()),STATUS_FAILURE); } if ((rep = consumer()->send_APF_message(reply)) != STATUS_SUCCESS) { ACE_DEBUG ((MY_DEBUG ACE_TEXT ("failed to send APF message:TcpForwardReplay\n"))); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return rep; } return STATUS_SUCCESS; } // read a tcp forward cancel request from the socket and process it. STATUS AMT_Tunnel_Supplier::process_tcp_forward_cancel() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_tcp_forward_cancel")); STATUS rep = STATUS_SUCCESS; APF_TcpForwardCancelRequest request; if ((rep = request.read(getPeer()))!= STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read tcp forward cancel request\n"))); return rep; } ACE_DEBUG ((MY_INFO ACE_TEXT ("Intel remote client %s canceled port forwarding to address %s:%d\n"), identifier(), request.addressToBind.c_str(), request.port)); { ACE_WRITE_GUARD_RETURN( ACE_RW_Thread_Mutex, lock, Tunnel_Manager::instance().guard(), STATUS_FAILURE); // Check if this address and port already exists in the tunnel manager. if (!Tunnel_Manager::instance ().tunnel_exists(request.addressToBind, request.port)) { ACE_DEBUG ((MY_DEBUG ACE_TEXT ("Tunnel doesn't exists in map\n"))); rep = consumer()->send_APF_message((ACE_UINT8)APF_REQUEST_FAILURE); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send APF message:TcpForwardCancel\n")); return STATUS_SUCCESS; } Port_Address port_add(request.addressToBind, request.port); tunnel_handler_->removeTcpForwardConnection(port_add); } // Send alert to MC if (sendAlertToSubscribMCList( mps__ConnectionStateTypeDefinition__DISCONNECTED, request.addressToBind, request.port) == -1) { ACE_DEBUG ((MY_WARNING ACE_TEXT("[%s] Failed to send SOAP notification on port forwarding cancellation\n"),identifier())); } rep = consumer()->send_APF_message((ACE_UINT8)APF_REQUEST_SUCCESS); CHECK_STATUS_REP(rep, ACE_TEXT ("failed to send APF message:TcpForwardCancelReplay\n")); return STATUS_SUCCESS; } // read udp send to request from the socket and process it. STATUS AMT_Tunnel_Supplier::process_udp_send_to() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_udp_send_to")); STATUS rep; //read UDP request: APF_UdpSendTo request; if ((rep = request.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read UDP send to message\n"))); return rep; } // Check if can send a message to that server using the filter hash // (specified in the configuration) const bool* authServersListNeeded = getAuthServersListNeeded(); if (authServersListNeeded == NULL) { ACE_DEBUG((MY_INFO ACE_TEXT("Failed to get if servers list is required\n"))); return STATUS_FAILURE; } if ((*authServersListNeeded) && !checkInFilter(request.hostStr, request.port)) { // Not found MC in filter hash - don't continue to send UDP message. ACE_DEBUG((MY_INFO ACE_TEXT("[%s] Did not send UDP to %s:%d - not in filter list\n"), identifier(), request.hostStr.c_str(), request.port)); // Filtering is still a success. return STATUS_SUCCESS; } //send UDP data ACE_SOCK_Dgram cli_dgram; ACE_INET_Addr mc_addr; mc_addr.set(request.port, request.hostStr.c_str()); if (cli_dgram.open(ACE_Addr::sap_any, mc_addr.get_type ()) == -1) { ACE_DEBUG((MY_DEBUG ACE_TEXT("protocol %d, SOCK_Dgram open FAILURE\n"), mc_addr.get_type())); // We are not closing the tunnel if the udp send failed return STATUS_RECOVER_ERROR; } else if (cli_dgram.send (request.data->base(), request.dataLength, mc_addr) == -1) { ACE_DEBUG((MY_WARNING ACE_TEXT("[%s] Intel remote client failed to send a UDP message to %s:%d\n"), identifier(),request.hostStr.c_str(),request.port)); // We are not closing the tunnel if the udp send failed cli_dgram.close(); return STATUS_RECOVER_ERROR; } cli_dgram.close(); ACE_DEBUG((MY_INFO ACE_TEXT("[%s] Intel remote client send a UDP message to %s:%d\n"), identifier(), request.hostStr.c_str(), request.port)); return STATUS_SUCCESS; } bool AMT_Tunnel_Supplier::checkInFilter(const ACE_CString& host, const ACE_UINT32& port) { // this check is a sanity check so no error will occur later. if (port > USHRT_MAX) return false; char bufPort[PORT_STR_LENGTH + 1]; ACE_OS::sprintf(bufPort, "%d", port); ACE_TString addressStr = host + DELIMITER_HOST_PORT + bufPort; AuthServerHash &filterHash = *getChangableAutherizedServersHash(); if (&filterHash == NULL) { ACE_DEBUG((MY_DEBUG ACE_TEXT("Failed to get changeable authorized server hash.\n"))); return false; } ACE_READ_GUARD_RETURN(ACE_RW_Thread_Mutex, lock, filterHash.guard(), false); return (filterHash.find(addressStr) != filterHash.end()); } // Handle channel open reply STATUS AMT_Tunnel_Supplier::process_channel_open_reply(ACE_UINT8 status) { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_channel_open_reply")); // Check tunnel state if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) { ACE_DEBUG((MY_DEBUG ACE_TEXT ("AMT_Tunnel_Supplier: channel open direct request with illegal state\n"))); return STATUS_INVALID_TUNNEL_STATE; } STATUS rep; APF_ChannelOpenReply reply; if ((rep = reply.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read channel open reply message\n"))); return rep; } ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex, lock, channel_manager().guard(),STATUS_FAILURE); // Get the channel from map AMT_Channel *ch = NULL; ch = channel_manager().find_channel(reply.recipientChannel, Channel_Manager::MPS_ENDPOINT); if (ch == NULL || ch->getState() != AMT_Channel::PENDING_OPEN) { // Channel doesn't exists - error ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier::channel_open_reply Channel doesn't exists\n"))); return STATUS_INVALID_CHANNEL; } // If confirmation if (status == APF_CHANNEL_OPEN_CONFIRMATION) { ACE_DEBUG((MY_INFO ACE_TEXT("Channel between Intel remote client %s and management console %s opened. Channel ID [%d:%d]\n"), identifier(), ((SocksConsumer*)(ch->getTcpConsumer()))->identifier(), reply.recipientChannel,reply.senderChannel)); ch->setAMTid(reply.senderChannel); if ((channel_manager().add_amt_id(reply.recipientChannel, reply.senderChannel)) != 0) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier::channel_open_reply invalid senderChannel\n"))); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return STATUS_ILLEGAL_APF_MESSAGE; } ch->setState(AMT_Channel::OPEN); // Send reply to consumers // We don't check the return code of the following, if a failure in the // tcp consumer/channel consumer occurred we expect the channel to be closed ((SocksConsumer*)(ch->getTcpConsumer()))->openRep(Tcp_Consumer::SUCCESS); ch->getChannelConsumer()->activateChannel(reply.senderChannel,reply.initialWindowSize); } else if (status == APF_CHANNEL_OPEN_FAILURE) { ACE_DEBUG((MY_WARNING ACE_TEXT("[%s] Intel remote client rejected connection request\n"),identifier())); // Delete channel from map channel_manager().remove_channel(reply.recipientChannel, Channel_Manager::MPS_ENDPOINT); ch->getChannelConsumer()->channelOpenFailure(); ((SocksConsumer*)ch->getTcpConsumer())->openRep(Tcp_Consumer::FAILURE); ch->getTcpConsumer()->connectionClose(); delete ch; } else { // impossible. sanity check ACE_ERROR((MY_DEBUG ACE_TEXT(" channel open reply failure. unknown status = %d\n"), status)); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return STATUS_ILLEGAL_APF_MESSAGE; } return STATUS_SUCCESS; } //------------------------------------------------- // Process a channel open request from AMT. //------------------------------------------------- STATUS AMT_Tunnel_Supplier::process_channel_open_direct() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_channel_open_direct")); if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) { ACE_DEBUG((MY_DEBUG ACE_TEXT ("AMT_Tunnel_Supplier channel open direct request with illegal state\n"))); return STATUS_INVALID_TUNNEL_STATE; } STATUS rep = STATUS_FAILURE; //read message: APF_ChannelOpenDirectRequest request; if ((rep = request.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read channel open direct request\n"))); return rep; } // Verify APF message if (request.channelTypeString.compare(APF_CHANNEL_OPEN_DIRECT_FORWARD) != 0) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received channel open direct request with invalid type (%s)\n"), request.channelTypeString.c_str())); return STATUS_ILLEGAL_APF_MESSAGE; } ACE_DEBUG((MY_DEBUG ACE_TEXT ("Received Channel Open Direct Request (%s:%d)\n"), request.targetHostString.c_str(), request.targetPort)); const bool* authServersListNeeded = getAuthServersListNeeded(); if (authServersListNeeded == NULL) { ACE_DEBUG((MY_DEBUG ACE_TEXT("Failed to get if servers list is required.\n"))); return STATUS_FAILURE; } if ((*authServersListNeeded) && !checkInFilter(request.targetHostString, request.targetPort)) { // Not found MC in filter hash - don't continue to send TCP message. ACE_DEBUG((MY_INFO ACE_TEXT("[%s] Did not send TCP to %s:%d - not in filter list\n"), identifier(), request.targetHostString.c_str(), request.targetPort)); //send failure reply: consumer()->channelOpenDirectRep(false,request.senderChannel, NULL, NULL); // Filtering is still a success. return STATUS_SUCCESS; } ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex, lock, channel_manager().guard(),STATUS_FAILURE); // Create a new pending channel AMT_Channel *ch = channel_manager().create_channel(request.senderChannel); if (ch == NULL) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier denied open direct request, invalid sender channel or no more channels available!!!\n"))); consumer()->channelOpenDirectRep(false,request.senderChannel, NULL, NULL); return STATUS_SUCCESS; } // Update the channel details ch->setWindowSize(*getMaximumWindowSize()); ch->setState(AMT_Channel::PENDING_OPEN); //create tcp: Tcp_Svc_Handler* tcp; ACE_NEW_RETURN (tcp, Tcp_Svc_Handler(request.initialWindowSize, ch->getAMTid(), consumer()), STATUS_MALLOC_FAILURE); //set tcp handlers addresses: ACE_INET_Addr remote_addr(request.targetPort, request.targetHostString.c_str(),AF_INET); tcp->remote_addr(remote_addr); ACE_INET_Addr local_addr(request.targetPort, request.originatorIpAddress.c_str(),AF_INET); tcp->local_addr(local_addr); //connect TCP handler: Connection_Handler_Connector* connector = &Connection_Handler_Connector::instance(); //NOTE: // the connection performed in sync way (blocking until connection will complete) if (connector->initiate_connection(tcp) == -1) { //send failure reply: consumer()->channelOpenDirectRep(false,ch->getAMTid(), NULL, NULL, ch->getMPSid()); channel_manager().remove_channel(ch->getMPSid(), Channel_Manager::MPS_ENDPOINT); delete ch; } return STATUS_SUCCESS; } // Process channel windows adjust message STATUS AMT_Tunnel_Supplier::process_channel_window_adjust() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_channel_window_adjust")); if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) { ACE_DEBUG((MY_DEBUG ACE_TEXT ("AMT_Tunnel_Supplier: received channel window adjust with illegal state\n"))); return STATUS_INVALID_TUNNEL_STATE; } STATUS rep; APF_ChannelWindowAdjust msg; AMT_Channel *ch = NULL; if ((rep = msg.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read channel window adjust message\n"))); return rep; } ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex, lock, channel_manager().guard(),STATUS_FAILURE); // Get the channel from the map ch = channel_manager().find_channel(msg.recipientChannel, Channel_Manager::MPS_ENDPOINT); if (ch == NULL) { // Channel doesn't exist - according do APF protocol return STATUS_SUCCESS; } ACE_DEBUG ((MY_DEBUG ACE_TEXT ("Channel window adjust: AMT(%d) => MC(%d), size = %d\n"), ch->getAMTid(), ch->getMPSid(), msg.bytesToAdd)); if (ch->getChannelConsumer() == NULL) { return STATUS_FATAL_ERROR; } // Here we give CPU time to the channel consumer (to put messages into our consumer) if (ch->getChannelConsumer()->setWinSize(msg.bytesToAdd) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("failed to set window size in process_channel_window_adjust"))); } return STATUS_SUCCESS; } // process channel data STATUS AMT_Tunnel_Supplier::process_channel_data() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_channel_data")); ACE_DEBUG((MY_TRACE ACE_TEXT("[%s] AMT_Tunnel_Supplier::process_channel_data\n"), identifier())); APF_ChannelData msg; STATUS rep = STATUS_SUCCESS; if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) { ACE_DEBUG((MY_DEBUG ACE_TEXT ("AMT_Tunnel_Supplier: received channel data with illegal state\n"))); return STATUS_INVALID_TUNNEL_STATE; } if ((rep = msg.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("[%s] AMT_Tunnel_Supplier: failed to read channel data message\n"), identifier())); return rep; } ACE_GUARD_RETURN(ACE_Recursive_Thread_Mutex, lock, channel_manager().guard(),STATUS_FAILURE); // Get the channel from map AMT_Channel *ch = NULL; ch = channel_manager().find_channel(msg.recipientChannel, Channel_Manager::MPS_ENDPOINT); if (ch == NULL) { ACE_DEBUG((MY_DEBUG ACE_TEXT("received data from AMT but failed to find the match channel\n"))); // Channel doesn't exists - error return STATUS_FAILURE; } else if ((ch->getState() != AMT_Channel::OPEN) && (ch->getState() != AMT_Channel::MC_CLOSED)) { ACE_DEBUG((MY_DEBUG ACE_TEXT("received data from AMT but channel not in OPEN state\n"))); return STATUS_FAILURE; } ACE_DEBUG ((MY_DEBUG ACE_TEXT ("[%s] Channel data: AMT(%d) => MC(%d), size = %d\n"), identifier(), ch->getAMTid(), ch->getMPSid(), msg.dataLen)); rep = ch->getTcpConsumer()->sendData(msg.data); if (rep != STATUS_SUCCESS) { ACE_DEBUG ((MY_DEBUG ACE_TEXT("failed to send data to MC\n"))); } if (ch->getWindowSize() < msg.dataLen) { ACE_DEBUG ((MY_DEBUG ACE_TEXT("received more data to send then window size\n"))); ch->setWindowSize(0); } else { ch->setWindowSize(ch->getWindowSize() - msg.dataLen); } if (ch->getWindowSize() <= *getMinimumWindowSize()) { APF_ChannelWindowAdjust win_adj; win_adj.recipientChannel = ch->getAMTid(); win_adj.bytesToAdd = *getMaximumWindowSize() - ch->getWindowSize(); rep = this->consumer()->send_APF_message(win_adj); CHECK_STATUS_REP(rep, ACE_TEXT("failed to send APF:Window Adjust message\n")); ch->setWindowSize(*getMaximumWindowSize()); ACE_DEBUG ((MY_DEBUG ACE_TEXT ("Channel window adjust: MC(%d) => AMT(%d), size = %d\n"), ch->getMPSid(), ch->getAMTid(), win_adj.bytesToAdd)); } return rep; } // Process channel close message STATUS AMT_Tunnel_Supplier::process_channel_close() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_channel_close")); ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier::process_channel_close\n"))); APF_ChannelClose msg; STATUS rep = STATUS_SUCCESS; if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) { ACE_DEBUG((MY_DEBUG ACE_TEXT ("AMT_Tunnel_Supplier: received channel close with illegal state\n"))); return STATUS_INVALID_TUNNEL_STATE; } if ((rep = msg.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read channel close message\n"))); return rep; } // Get lock on channel manager ACE_GUARD_RETURN( ACE_Recursive_Thread_Mutex, lock, channel_manager().guard(), STATUS_FAILURE); // Get the channel from map AMT_Channel *ch = NULL; ch = channel_manager().find_channel(msg.recipientChannel, Channel_Manager::MPS_ENDPOINT); if (ch == NULL) { // Channel doesn't exists - error ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received channel close on invalid channel\n"))); return STATUS_FAILURE; } Tcp_Consumer* tcp_consumer = ch->getTcpConsumer(); ch->setTcpConsumer(NULL); // The MC send close request first, we can delete this channel if (ch->getState() == AMT_Channel::MC_CLOSED) { ACE_DEBUG((MY_INFO ACE_TEXT("Channel between Intel remote client %s and management console %s closed. Channel ID [%d:%d]\n"), identifier(), tcp_consumer->identifier(),ch->getAMTid(),ch->getMPSid())); if (ch->getChannelConsumer() != NULL) { ch->getChannelConsumer()->connectionUnbind(); } // Remove this channel from both maps channel_manager().remove_channel(msg.recipientChannel, Channel_Manager::MPS_ENDPOINT); delete ch; } // Check if AMT requested to close this channel else if (ch->getState() == AMT_Channel::OPEN) { ch->setState(AMT_Channel::TUNNEL_CLOSED); // Notify this channel consumer that we are closed ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: Channel close MC(%d)\n"), msg.recipientChannel)); ch->getChannelConsumer()->channelClose(); } else { // received close channel on illegal state ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: received close channel on illegal state\n"))); return STATUS_FAILURE; } lock.release(); // Notify consumer to close if (tcp_consumer!= NULL) { tcp_consumer->connectionClose(); } return STATUS_SUCCESS; } // Close this supplier, called from the reactor after read mask close STATUS AMT_Tunnel_Supplier::handle_close() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::close")); return STATUS_CONNECTION_CLOSED; } STATUS AMT_Tunnel_Supplier::process_KeepAliveReq() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_KeepAliveReq")); APF_KeepAliveReq msg; STATUS rep = STATUS_SUCCESS; //if (tunnelState() != AMT_Tunnel_Handler::PFWD_OPEN) //{ // ACE_DEBUG((MY_ERROR // ACE_TEXT ("AMT_Tunnel_Supplier: received channel close with illegal state\n"))); // return STATUS_INVALID_TUNNEL_STATE; //} if ((rep = msg.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read keepAlive message\n"))); return rep; } APF_KeepAliveReply reply; reply._cookie = msg._cookie; if ((rep = consumer()->send_APF_message(reply)) != STATUS_SUCCESS) { ACE_DEBUG ((MY_DEBUG ACE_TEXT ("failed to send APF message:KeepAliveReply\n"))); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return rep; } return STATUS_SUCCESS; } STATUS AMT_Tunnel_Supplier::process_KeepAliveOptionReq() { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::process_KeepAliveOptionReq")); APF_KeepAliveOptionReq msg; STATUS rep = STATUS_SUCCESS; if ((rep = msg.read(getPeer())) != STATUS_SUCCESS) { ACE_DEBUG((MY_DEBUG ACE_TEXT("AMT_Tunnel_Supplier: failed to read keepAlive message\n"))); return rep; } APF_KeepAliveOptionReply reply; reply._keepalive_interval = 0; reply._read_timeout = 0; if ((rep = consumer()->send_APF_message(reply)) != STATUS_SUCCESS) { ACE_DEBUG ((MY_DEBUG ACE_TEXT ("failed to send APF message:KeepAliveReply\n"))); setDisconnectReason(APF_DISCONNECT_PROTOCOL_ERROR); return rep; } return STATUS_SUCCESS; } //----------------------------------------- // Parses 16 bytes of UUID sent from AMT // device in hello message to UUID string format. // // Arguments: // @binUUID - UUID to convert // @textUUID - [out] UUID string in correct format // //----------------------------------------- void StringFromUUID(const unsigned char binUUID[16], unsigned char textUUID[TEXT_UUID_SIZE]) { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::StringFromUUID")); unsigned char *hexString = (unsigned char *)binUUID; int res = sprintf_s((char *)textUUID, TEXT_UUID_SIZE, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", *(unsigned long *) (hexString), *(unsigned short *)(hexString+4), *(unsigned short *)(hexString+4+2), *(unsigned char *) (hexString+4+2+2), *(unsigned char *) (hexString+4+2+2+1), *(unsigned char *) (hexString+4+2+2+2), *(unsigned char *) (hexString+4+2+2+3), *(unsigned char *) (hexString+4+2+2+4), *(unsigned char *) (hexString+4+2+2+5), *(unsigned char *) (hexString+4+2+2+6), *(unsigned char *) (hexString+4+2+2+7) ); if (res == -1) { ACE_ERROR((MY_ERROR ACE_TEXT(" Failed to parse UUID sent from AMT to string\n"))); } } STATUS AMT_Tunnel_Supplier::sendAlertToSubscribMCList( mps__ConnectionStateTypeDefinition state, ACE_CString &fqdn, unsigned short port) { ACE_TRACE(ACE_TEXT("AMT_Tunnel_Supplier::sendAlertToSubscribMCList")); ACE_CString str_state; if (state == mps__ConnectionStateTypeDefinition__CONNECTED) str_state = "CONNECT"; else str_state = "DISCONNECT"; unsigned char textUUID[TEXT_UUID_SIZE]; StringFromUUID(UUID_, textUUID); ACE_CString fqdn_upper_case=toUpper(fqdn); ACE_DEBUG((MY_DEBUG ACE_TEXT("Sending notification to management console\n\tDevice = %s:%d - %s\n"), fqdn_upper_case.c_str(), port, str_state.c_str())); string temp_fqdn(fqdn_upper_case.c_str()); string temp_uuid((char*)textUUID); string temp_hostname((*getMCSocksListenIP()).c_str()); const unsigned short* mcHttpListenPort = getMCHttpListenPort(); const unsigned short* mcSocksListenPort = getMCSocksListenPort(); if (mcHttpListenPort == NULL || mcSocksListenPort == NULL) { ACE_DEBUG((MY_DEBUG ACE_TEXT("Failed to get MC authentication details.\n"))); return STATUS_FAILURE; } DevicePresence::instance().sendEvent( temp_fqdn, port, temp_uuid, temp_hostname, *mcHttpListenPort, *mcSocksListenPort, state); return STATUS_SUCCESS; } const char* AMT_Tunnel_Supplier::identifier() { return tunnel_handler_->identifier(); }