ua_debug_dump_pkgs_file.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. *
  5. * Copyright 2019 (c) fortiss (Author: Stefan Profanter)
  6. */
  7. /**
  8. * This code is used to generate a binary file for every request type
  9. * which can be sent from a client to the server.
  10. * These files form the basic corpus for fuzzing the server.
  11. */
  12. #ifndef UA_DEBUG_DUMP_PKGS_FILE
  13. #error UA_DEBUG_DUMP_PKGS_FILE must be defined
  14. #endif
  15. #include <open62541/transport_generated_encoding_binary.h>
  16. #include <open62541/types.h>
  17. #include <open62541/types_generated_encoding_binary.h>
  18. #include "server/ua_server_internal.h"
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <unistd.h>
  22. // This number is added to the end of every corpus data as 4 bytes.
  23. // It allows to generate valid corpus and then the fuzzer will use
  24. // these last 4 bytes to determine the simulated available RAM.
  25. // The fuzzer will then fiddle around with this number and (hopefully)
  26. // make it smaller, so that we can simulate Out-of-memory errors.
  27. #define UA_DUMP_RAM_SIZE 8 * 1024 * 1024
  28. unsigned int UA_dump_chunkCount = 0;
  29. char *UA_dump_messageTypes[] = {"ack", "hel", "msg", "opn", "clo", "err", "unk"};
  30. struct UA_dump_filename {
  31. const char *messageType;
  32. char serviceName[100];
  33. };
  34. void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection,
  35. UA_ByteString *messageBuffer);
  36. /**
  37. * Gets a pointer to the string representing the given message type from UA_dump_messageTypes.
  38. * Used for naming the dumped file.
  39. */
  40. static const char *
  41. UA_debug_dumpGetMessageTypePrefix(UA_UInt32 messageType) {
  42. switch(messageType & 0x00ffffff) {
  43. case UA_MESSAGETYPE_ACK:
  44. return UA_dump_messageTypes[0];
  45. case UA_MESSAGETYPE_HEL:
  46. return UA_dump_messageTypes[1];
  47. case UA_MESSAGETYPE_MSG:
  48. return UA_dump_messageTypes[2];
  49. case UA_MESSAGETYPE_OPN:
  50. return UA_dump_messageTypes[3];
  51. case UA_MESSAGETYPE_CLO:
  52. return UA_dump_messageTypes[4];
  53. case UA_MESSAGETYPE_ERR:
  54. return UA_dump_messageTypes[5];
  55. default:
  56. return UA_dump_messageTypes[6];
  57. }
  58. }
  59. /**
  60. * Decode the request message type from the given byte string and
  61. * set the global requestServiceName variable to the name of the request.
  62. * E.g. `GetEndpointsRequest`
  63. */
  64. static UA_StatusCode
  65. UA_debug_dumpSetServiceName(const UA_ByteString *msg, char serviceNameTarget[100]) {
  66. /* At 0, the nodeid starts... */
  67. size_t offset = 0;
  68. /* Decode the nodeid */
  69. UA_NodeId requestTypeId;
  70. UA_StatusCode retval = UA_NodeId_decodeBinary(msg, &offset, &requestTypeId);
  71. if(retval != UA_STATUSCODE_GOOD)
  72. return retval;
  73. if(requestTypeId.identifierType != UA_NODEIDTYPE_NUMERIC || requestTypeId.namespaceIndex != 0) {
  74. snprintf(serviceNameTarget, 100, "invalid_request_id");
  75. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  76. }
  77. const UA_DataType *requestType = NULL;
  78. for (size_t i=0; i<UA_TYPES_COUNT; i++) {
  79. if (UA_TYPES[i].binaryEncodingId == requestTypeId.identifier.numeric) {
  80. requestType = &UA_TYPES[i];
  81. break;
  82. }
  83. }
  84. if (requestType == NULL) {
  85. snprintf(serviceNameTarget, 100, "invalid_request_no_type");
  86. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  87. }
  88. snprintf(serviceNameTarget, 100, "_%s", requestType->typeName);
  89. return UA_STATUSCODE_GOOD;
  90. }
  91. /**
  92. * We need to decode the given binary message to get the name of the called service.
  93. * This method is used if the connection has no channel yet.
  94. */
  95. static UA_StatusCode
  96. UA_debug_dump_setName_withoutChannel(UA_Server *server, UA_Connection *connection,
  97. UA_ByteString *message, struct UA_dump_filename* dump_filename) {
  98. size_t offset = 0;
  99. UA_TcpMessageHeader tcpMessageHeader;
  100. UA_StatusCode retval =
  101. UA_TcpMessageHeader_decodeBinary(message, &offset, &tcpMessageHeader);
  102. if(retval != UA_STATUSCODE_GOOD)
  103. return retval;
  104. dump_filename->messageType =
  105. UA_debug_dumpGetMessageTypePrefix(tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff);
  106. if ((tcpMessageHeader.messageTypeAndChunkType & 0x00ffffff) == UA_MESSAGETYPE_MSG) {
  107. // this should not happen in normal operation
  108. UA_LOG_ERROR(&server->config.logger, UA_LOGCATEGORY_SERVER, "Got MSG package without channel.");
  109. return UA_STATUSCODE_BADUNEXPECTEDERROR;
  110. }
  111. return UA_STATUSCODE_GOOD;
  112. }
  113. /**
  114. * We need to decode the given binary message to get the name of the called service.
  115. * This method is used if the connection an established secure channel.
  116. *
  117. * message is the decoded message starting at the nodeid of the content type.
  118. */
  119. static void
  120. UA_debug_dump_setName_withChannel(void *application, UA_SecureChannel *channel,
  121. UA_MessageType messagetype, UA_UInt32 requestId,
  122. const UA_ByteString *message) {
  123. struct UA_dump_filename *dump_filename = (struct UA_dump_filename *)application;
  124. dump_filename->messageType = UA_debug_dumpGetMessageTypePrefix(messagetype);
  125. if(messagetype == UA_MESSAGETYPE_MSG)
  126. UA_debug_dumpSetServiceName(message, dump_filename->serviceName);
  127. }
  128. /**
  129. * Called in processCompleteChunk for every complete chunk which is received by the server.
  130. *
  131. * It will first try to decode the message to get the name of the called service.
  132. * When we have a name the message is dumped as binary to that file.
  133. * If the file already exists a new file will be created with a counter at the end.
  134. */
  135. void
  136. UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const connection,
  137. UA_ByteString *messageBuffer) {
  138. struct UA_dump_filename dump_filename;
  139. dump_filename.messageType = NULL;
  140. dump_filename.serviceName[0] = 0;
  141. if(!connection->channel) {
  142. UA_debug_dump_setName_withoutChannel(server, connection, messageBuffer, &dump_filename);
  143. } else {
  144. UA_SecureChannel dummy = *connection->channel;
  145. TAILQ_INIT(&dummy.messages);
  146. UA_ByteString messageBufferCopy;
  147. UA_ByteString_copy(messageBuffer, &messageBufferCopy);
  148. UA_SecureChannel_decryptAddChunk(&dummy, &messageBufferCopy, UA_TRUE);
  149. UA_SecureChannel_processCompleteMessages(&dummy, &dump_filename, UA_debug_dump_setName_withChannel);
  150. UA_SecureChannel_deleteMessages(&dummy);
  151. UA_ByteString_deleteMembers(&messageBufferCopy);
  152. }
  153. char fileName[250];
  154. snprintf(fileName, sizeof(fileName), "%s/%05u_%s%s", UA_CORPUS_OUTPUT_DIR, ++UA_dump_chunkCount,
  155. dump_filename.messageType ? dump_filename.messageType : "", dump_filename.serviceName);
  156. char dumpOutputFile[266];
  157. snprintf(dumpOutputFile, 255, "%s.bin", fileName);
  158. // check if file exists and if yes create a counting filename to avoid overwriting
  159. unsigned cnt = 1;
  160. while ( access( dumpOutputFile, F_OK ) != -1 ) {
  161. snprintf(dumpOutputFile, 266, "%s_%u.bin", fileName, cnt);
  162. cnt++;
  163. }
  164. UA_LOG_INFO(&server->config.logger, UA_LOGCATEGORY_SERVER,
  165. "Dumping package %s", dumpOutputFile);
  166. FILE *write_ptr = fopen(dumpOutputFile, "ab");
  167. fwrite(messageBuffer->data, messageBuffer->length, 1, write_ptr); // write 10 bytes from our buffer
  168. // add the available memory size. See the UA_DUMP_RAM_SIZE define for more info.
  169. uint32_t ramSize = UA_DUMP_RAM_SIZE;
  170. fwrite(&ramSize, sizeof(ramSize), 1, write_ptr);
  171. fclose(write_ptr);
  172. }