/********************************************************************************/ /* */ /* Socket Interface to a TPM Simulator */ /* Written by Ken Goldman */ /* IBM Thomas J. Watson Research Center */ /* $Id: TcpServerPosix.c 1658 2021-01-22 23:14:01Z kgoldman $ */ /* */ /* Licenses and Notices */ /* */ /* 1. Copyright Licenses: */ /* */ /* - Trusted Computing Group (TCG) grants to the user of the source code in */ /* this specification (the "Source Code") a worldwide, irrevocable, */ /* nonexclusive, royalty free, copyright license to reproduce, create */ /* derivative works, distribute, display and perform the Source Code and */ /* derivative works thereof, and to grant others the rights granted herein. */ /* */ /* - The TCG grants to the user of the other parts of the specification */ /* (other than the Source Code) the rights to reproduce, distribute, */ /* display, and perform the specification solely for the purpose of */ /* developing products based on such documents. */ /* */ /* 2. Source Code Distribution Conditions: */ /* */ /* - Redistributions of Source Code must retain the above copyright licenses, */ /* this list of conditions and the following disclaimers. */ /* */ /* - Redistributions in binary form must reproduce the above copyright */ /* licenses, this list of conditions and the following disclaimers in the */ /* documentation and/or other materials provided with the distribution. */ /* */ /* 3. Disclaimers: */ /* */ /* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */ /* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */ /* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */ /* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */ /* Contact TCG Administration (admin@trustedcomputinggroup.org) for */ /* information on specification licensing rights available through TCG */ /* membership agreements. */ /* */ /* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */ /* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */ /* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */ /* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */ /* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */ /* */ /* - Without limitation, TCG and its members and licensors disclaim all */ /* liability, including liability for infringement of any proprietary */ /* rights, relating to use of information in this specification and to the */ /* implementation of this specification, and TCG disclaims all liability for */ /* cost of procurement of substitute goods or services, lost profits, loss */ /* of use, loss of data or any incidental, consequential, direct, indirect, */ /* or special damages, whether under contract, tort, warranty or otherwise, */ /* arising in any way out of use or reliance upon this specification or any */ /* information herein. */ /* */ /* (c) Copyright IBM Corp. and others, 2012 - 2021 */ /* */ /********************************************************************************/ #include "simulatorPrivate.h" // D.3 TcpServer.c // D.3.1. Description // This file contains the socket interface to a TPM simulator. // D.3.2. Includes, Locals, Defines and Function Prototypes #include #include #include #include #include #include #include #include "string.h" #include #include #include #include "TpmTcpProtocol.h" #include "Manufacture_fp.h" #include "TcpServerPosix_fp.h" #include "Simulator_fp.h" #include "TpmProfile.h" /* kgold */ //#include "Platform_fp.h" /* kgold */ #include "PlatformACT_fp.h" /* added kgold */ bool /* kgold */ ReadUINT32( SOCKET s, uint32_t *val ); #define IPVER(len) ((len) == sizeof(struct sockaddr_in6) ? 6 : \ ((len) == sizeof(struct sockaddr_in) ? 4 : 0)) #ifndef __IGNORE_STATE__ static uint32_t ServerVersion = 1; #define MAX_BUFFER 1048576 char InputBuffer[MAX_BUFFER]; //The input data buffer for the simulator. char OutputBuffer[MAX_BUFFER]; //The output data buffer for the simulator. struct { uint32_t largestCommandSize; uint32_t largestCommand; uint32_t largestResponseSize; uint32_t largestResponse; } CommandResponseSizes = {0}; #endif // __IGNORE_STATE___ // D.3.3. Functions // D.3.3.1. CreateSocket() // This function creates a socket listening on PortNumber. static int CreateSocket( int PortNumber, SOCKET *listenSocket, socklen_t *addr_len, int domain // AF_INET or AF_INET6 ) { struct sockaddr_in MyAddress4; struct sockaddr_in6 MyAddress6; int opt; int res; // create listening socket *listenSocket = socket(domain, SOCK_STREAM, 0); if(*listenSocket == -1) { printf("Warning: Cannot create server listen %s socket\nWarning is %d %s\n", domain == AF_INET6 ? "IPv6" : (domain == AF_INET) ? "IPv4" : "?", errno, strerror(errno)); printf("Ignore the IPv6 warning if the platform doesn't support IPv6\n"); return -1; } opt = 1; /* Set SO_REUSEADDR before calling bind() for servers that bind to a fixed port number. */ res = setsockopt(*listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (res != 0) { printf("setsockopt error. Error is %d %s\n", errno, strerror(errno)); return -1; } // bind the listening socket to the specified port, any address (0s) switch (domain) { case AF_INET: memset((char *)&MyAddress4, 0, sizeof(MyAddress4)); MyAddress4.sin_family = domain; MyAddress4.sin_port = htons((short) PortNumber); *addr_len = sizeof(struct sockaddr_in); res= bind(*listenSocket,(struct sockaddr*) &MyAddress4, *addr_len); break; case AF_INET6: memset((char *)&MyAddress6, 0, sizeof(MyAddress6)); MyAddress6.sin6_family = domain; MyAddress6.sin6_port = htons((short) PortNumber); *addr_len = sizeof(struct sockaddr_in6); opt = 1; // Set IPPROTO_IPV6 so that it's just for IPv6 and not both IPv4/IPv6. res = setsockopt(*listenSocket, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)); if (res != 0) { printf("setsockopt IPV6_V6ONLY error. Error is %d %s\n", errno, strerror(errno)); return -1; } res= bind(*listenSocket,(struct sockaddr*) &MyAddress6, *addr_len); break; default: printf("Address family %d not supported\n", domain); return -1; } if(res != 0) { close(*listenSocket); *listenSocket = -1; printf("Bind error. Error is %d %s\n", errno, strerror(errno)); return -1; } // listen/wait for server connections res= listen(*listenSocket,3); if(res != 0) { close(*listenSocket); *listenSocket = -1; printf("Listen error. Error is %d %s\n", errno, strerror(errno)); return -1; } return 0; } // D.3.3.2. PlatformServer() // This function processes incoming platform requests. bool PlatformServer( SOCKET s ) { bool ok = true; uint32_t Command; for(;;) { ok = ReadBytes(s, (char*) &Command, 4); // client disconnected (or other error). We stop processing this client // and return to our caller who can stop the server or listen for another // connection. if(!ok) return true; Command = ntohl(Command); switch(Command) { case TPM_SIGNAL_POWER_ON: _rpc__Signal_PowerOn(false); break; case TPM_SIGNAL_POWER_OFF: _rpc__Signal_PowerOff(); break; case TPM_SIGNAL_RESET: _rpc__Signal_PowerOn(true); break; case TPM_SIGNAL_PHYS_PRES_ON: _rpc__Signal_PhysicalPresenceOn(); break; case TPM_SIGNAL_PHYS_PRES_OFF: _rpc__Signal_PhysicalPresenceOff(); break; case TPM_SIGNAL_CANCEL_ON: _rpc__Signal_CancelOn(); break; case TPM_SIGNAL_CANCEL_OFF: _rpc__Signal_CancelOff(); break; case TPM_SIGNAL_NV_ON: _rpc__Signal_NvOn(); break; case TPM_SIGNAL_NV_OFF: _rpc__Signal_NvOff(); break; case TPM_SESSION_END: // Client signaled end-of-session TpmEndSimulation(); return true; case TPM_STOP: // Client requested the simulator to exit return false; case TPM_TEST_FAILURE_MODE: _rpc__ForceFailureMode(); break; case TPM_GET_COMMAND_RESPONSE_SIZES: ok = WriteVarBytes(s, (char *)&CommandResponseSizes, sizeof(CommandResponseSizes)); memset(&CommandResponseSizes, 0, sizeof(CommandResponseSizes)); if(!ok) return true; break; case TPM_ACT_GET_SIGNALED: { uint32_t actHandle; ok = ReadUINT32(s, &actHandle); WriteUINT32(s, _rpc__ACT_GetSignaled(actHandle)); break; } default: printf("Unrecognized platform interface command %08x\n", Command); WriteUINT32(s, 1); return true; } WriteUINT32(s,0); } return false; } // D.3.3.3. PlatformSvcRoutine() // This function is called to set up the socket interfaces to listen for commands. int PlatformSvcRoutine( void *port ) { int PortNumber = *(int *)port; SOCKET listenSocket[2], maxListenSocket, serverSocket; struct sockaddr_storage HerAddress; fd_set sockSet; int res, i; int nSock = 0; socklen_t length[2]; BOOL continueServing = true; /* kgold initialized */ if (CreateSocket(PortNumber, &listenSocket[nSock], &length[nSock], AF_INET) == 0) { nSock++; } if (CreateSocket(PortNumber, &listenSocket[nSock], &length[nSock], AF_INET6) == 0) { nSock++; } if (nSock == 0) { printf("Create platform service socket fail\n"); exit(5); } maxListenSocket = listenSocket[0]; if ((nSock == 2) && (listenSocket[1] > maxListenSocket)) { maxListenSocket = listenSocket[1]; } // Loop accepting connections one-by-one until we are killed or asked to stop // Note the platform service is single-threaded so we don't listen for a new // connection until the prior connection drops. do { printf("Platform server listening on port %d\n", PortNumber); // Select both IPv4 and IPv6 sockets or whatever is available FD_ZERO(&sockSet); FD_SET(listenSocket[0], &sockSet); if (nSock == 2) FD_SET(listenSocket[1], &sockSet); do { res = select(maxListenSocket + 1, &sockSet, NULL, NULL, NULL); } while ((res == -1) && (errno == EINTR)); if (res == -1) { printf("Platform server select error. Error is %d %s\n", errno, strerror(errno)); return -1; } for (i = 0; i < nSock; i++) { int ipver = IPVER(length[i]); if (!FD_ISSET(listenSocket[i], &sockSet)) { continue; } // blocking accept serverSocket = accept(listenSocket[i], (struct sockaddr*) &HerAddress, &length[i]); if(serverSocket < 0) { printf("Platform server IPv%d Accept error. Error is %d %s\n", ipver, errno, strerror(errno)); return -1; }; printf("Platform IPv%d client accepted\n", ipver); // normal behavior on client disconnection is to wait for a new // client to connect continueServing = PlatformServer(serverSocket); close(serverSocket); serverSocket = -1; } } while(continueServing); return 0; } // D.3.3.4. PlatformSignalService() // This function starts a new thread waiting for platform signals. Platform signals are processed // one at a time in the order in which they are received. int PlatformSignalService( int *PortNumberPlatform ) { unsigned long thread; int irc = 0; pthread_t *pthread = (pthread_t *)&thread; irc = pthread_create(pthread, NULL, (void * (*)(void *))PlatformSvcRoutine, /* thread entry function */ (void *)PortNumberPlatform); /* thread function parameters */ if (irc != 0) { printf("Thread Creation failed\n"); return -1; } return 0; } // D.3.3.5. RegularCommandService() // This funciton services regular commands. int RegularCommandService( int *PortNumber ) { SOCKET listenSocket[2], maxListenSocket; SOCKET serverSocket; struct sockaddr_storage HerAddress; fd_set sockSet; int res, i; int nSock = 0; socklen_t length[2]; BOOL continueServing = true; /* kgold - initialized */ if (CreateSocket(*PortNumber, &listenSocket[nSock], &length[nSock], AF_INET) == 0) { nSock++; } if (CreateSocket(*PortNumber, &listenSocket[nSock], &length[nSock], AF_INET6) == 0) { nSock++; } if (nSock == 0) { printf("Create TPM command service socket fail\n"); return -1; } maxListenSocket = listenSocket[0]; if (nSock == 2 && listenSocket[1] > maxListenSocket) { maxListenSocket = listenSocket[1]; } // Loop accepting connections one-by-one until we are killed or asked to stop // Note the TPM command service is single-threaded so we don't listen for // a new connection until the prior connection drops. do { printf("TPM command server listening on port %d\n", *PortNumber); // Select both IPv4 and IPv6 sockets or whatever is available FD_ZERO(&sockSet); FD_SET(listenSocket[0], &sockSet); if (nSock == 2) FD_SET(listenSocket[1], &sockSet); do { res = select(maxListenSocket + 1, &sockSet, NULL, NULL, NULL); } while ((res == -1) && (errno == EINTR)); if (res == -1) { printf("TPM command server select error. Error is %d %s\n", errno, strerror(errno)); return -1; } for (i = 0; i < nSock; i++) { int ipver = IPVER(length[i]); if (!FD_ISSET(listenSocket[i], &sockSet)) continue; // blocking accept serverSocket = accept(listenSocket[i], (struct sockaddr*) &HerAddress, &length[i]); if(serverSocket < 0) { printf("TPM server IPv%d Accept error. Error is %d %s\n", ipver, errno, strerror(errno)); return -1; }; printf("Command IPv%d client accepted\n", ipver); // normal behavior on client disconnection is to wait for a new // client to connect continueServing = TpmServer(serverSocket); close(serverSocket); serverSocket = -1; } } while(continueServing); return 0; } #if RH_ACT_0 /* D.3.2.5. SimulatorTimeServiceRoutine() */ /* This function is called to service the time ticks. */ static int SimulatorTimeServiceRoutine( void ) { // All time is in ms const int64_t tick = 1000; uint64_t prevTime = _plat__RealTime(); int64_t timeout = tick; while (true) { uint64_t curTime; struct timespec req = {timeout / 1000, (timeout % 1000) * 1000}; struct timespec rem; nanosleep(&req, &rem); curTime = _plat__RealTime(); // May need to issue several ticks if the Sleep() took longer than asked, // or no ticks at all, it Sleep() was interrupted prematurely. while (prevTime < curTime - tick / 2) { //printf("%05lld | %05lld\n", // prevTime % 100000, (curTime - tick / 2) % 100000); _plat__ACT_Tick(); prevTime += (uint64_t)tick; } // Adjust the next timeout to keep the average interval of one second timeout = tick + (prevTime - curTime); //prevTime = curTime; //printf("%04lld | c:%05lld | p:%05llu\n", // timeout, curTime % 100000, prevTime); } return 0; } /* D.3.2.6. ActTimeService() */ /* This function starts a new thread waiting to wait for time ticks. */ /* Return Value Meaning */ /* ==0 success */ /* !=0 failure */ static int ActTimeService( void ) { static bool running = false; int ret = 0; if(!running) { pthread_t thread_id; /* kgold - hoisted out of WIndows only version */ printf("Starting ACT thread...\n"); // Don't allow ticks to be processed before TPM is manufactured. _plat__ACT_EnableTicks(false); // ret = pthread_create(&thread_id, NULL, (void*)SimulatorTimeServiceRoutine, NULL); if(ret != 0) printf("ACT thread Creation failed\n"); else running = TRUE; } return ret; } #endif // RH_ACT_0 // D.3.3.6. StartTcpServer() // Main entry-point to the TCP server. The server listens on port specified. Note that there is no // way to specify the network interface in this implementation. int StartTcpServer( int *PortNumber, int *PortNumberPlatform ) { int res; #if RH_ACT_0 || 1 // Start the Time Service routine res = ActTimeService(); if(res != 0) { printf("TimeService failed\n"); return res; } #endif // Start Platform Signal Processing Service res = PlatformSignalService(PortNumberPlatform); if (res != 0) { printf("PlatformSignalService failed\n"); return res; } // Start Regular/DRTM TPM command service res = RegularCommandService(PortNumber); if (res != 0) { printf("RegularCommandService failed\n"); return res; } return 0; } // D.3.3.7. ReadBytes() // This function reads the indicated number of bytes (NumBytes) into buffer from the indicated // socket. bool ReadBytes( SOCKET s, char *buffer, int NumBytes ) { int res; int numGot = 0; while(numGotMaxLen) { printf("Buffer too big. Client says %d\n", length); return false; } if(length==0) return true; res = ReadBytes(s, buffer, length); if(!res) return res; return true; } // D.3.3.11. WriteVarBytes() // Send a uint32_t--length-prepended binary array. Note that the 4-byte length is in network byte // order (big-endian). bool WriteVarBytes( SOCKET s, char *buffer, int BytesToSend ) { uint32_t netLength = htonl(BytesToSend); bool res; res = WriteBytes(s, (char*) &netLength, 4); if(!res) return res; res = WriteBytes(s, buffer, BytesToSend); if(!res) return res; return true; } // D.3.3.12. TpmServer() // Processing incoming TPM command requests using the protocol / interface defined above. bool TpmServer( SOCKET s ) { uint32_t length; uint32_t Command; uint8_t locality; bool ok; int result; int clientVersion; _IN_BUFFER InBuffer; _OUT_BUFFER OutBuffer; for(;;) { ok = ReadBytes(s, (char*) &Command, 4); // client disconnected (or other error). We stop processing this client // and return to our caller who can stop the server or listen for another // connection. if(!ok) return true; Command = ntohl(Command); switch(Command) { case TPM_SIGNAL_HASH_START: _rpc__Signal_Hash_Start(); break; case TPM_SIGNAL_HASH_END: _rpc__Signal_HashEnd(); break; case TPM_SIGNAL_HASH_DATA: ok = ReadVarBytes(s, InputBuffer, &length, MAX_BUFFER); if(!ok) return true; InBuffer.Buffer = (uint8_t *) InputBuffer; InBuffer.BufferSize = length; _rpc__Signal_Hash_Data(InBuffer); break; case TPM_SEND_COMMAND: ok = ReadBytes(s, (char*) &locality, 1); if(!ok) return true; ok = ReadVarBytes(s, InputBuffer, &length, MAX_BUFFER); if(!ok) return true; InBuffer.Buffer = (uint8_t *) InputBuffer; InBuffer.BufferSize = length; OutBuffer.BufferSize = MAX_BUFFER; OutBuffer.Buffer = (_OUTPUT_BUFFER) OutputBuffer; // record the number of bytes in the command if it is the largest // we have seen so far. if(InBuffer.BufferSize > CommandResponseSizes.largestCommandSize) { CommandResponseSizes.largestCommandSize = InBuffer.BufferSize; memcpy(&CommandResponseSizes.largestCommand, &InputBuffer[6], sizeof(uint32_t)); } _rpc__Send_Command(locality, InBuffer, &OutBuffer); // record the number of bytes in the response if it is the largest // we have seen so far. if(OutBuffer.BufferSize > CommandResponseSizes.largestResponseSize) { CommandResponseSizes.largestResponseSize = OutBuffer.BufferSize; memcpy(&CommandResponseSizes.largestResponse, &OutputBuffer[6], sizeof(uint32_t)); } ok = WriteVarBytes(s, (char*) OutBuffer.Buffer, OutBuffer.BufferSize); if(!ok) return true; break; case TPM_REMOTE_HANDSHAKE: ok = ReadBytes(s, (char*)&clientVersion, 4); if(!ok) return true; if( clientVersion == 0 ) { printf("Unsupported client version (0).\n"); return true; } ok &= WriteUINT32(s, ServerVersion); ok &= WriteUINT32(s, tpmInRawMode | tpmPlatformAvailable | tpmSupportsPP); break; case TPM_SET_ALTERNATIVE_RESULT: ok = ReadBytes(s, (char*)&result, 4); if(!ok) return true; // Alternative result is not applicable to the simulator. break; case TPM_SESSION_END: // Client signaled end-of-session return true; case TPM_STOP: // Client requested the simulator to exit return false; default: printf("Unrecognized TPM interface command %08x\n", Command); return true; } ok = WriteUINT32(s,0); if(!ok) return true; } }