modbus_server_security.c
Go to the documentation of this file.
1 /**
2  * @file modbus_server_security.c
3  * @brief Modbus/TCP security layer
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL MODBUS_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "modbus/modbus_server.h"
38 #include "debug.h"
39 
40 //Check TCP/IP stack configuration
41 #if (MODBUS_SERVER_SUPPORT == ENABLED && MODBUS_SERVER_TLS_SUPPORT == ENABLED)
42 
43 //Modbus Role OID (1.3.6.1.4.1.50316.802.1)
44 const uint8_t MODBUS_ROLE_OID[11] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x83, 0x89, 0x0C, 0x86, 0x22, 0x01};
45 
46 
47 /**
48  * @brief Parse client's certificate
49  * @param[in] tlsContext Pointer to the TLS context
50  * @param[in] certInfo Pointer to the X.509 certificate
51  * @param[in] pathLen Certificate path length
52  * @param[in] param Handle referencing a Modbus/TCP client connection
53  * @return Error code
54  **/
55 
57  const X509CertInfo *certInfo, uint_t pathLen, void *param)
58 {
59  error_t error;
60  size_t n;
61  size_t length;
62  const uint8_t *data;
63  ModbusClientConnection *connection;
64  X509Extension extension;
65 
66  //Point to the client connection
67  connection = (ModbusClientConnection *) param;
68 
69  //End-user certificate?
70  if(pathLen == 0)
71  {
72  //The X.509 v3 certificate format also allows communities to define
73  //private extensions to carry information unique to those communities
74  data = certInfo->tbsCert.extensions.raw.value;
75  length = certInfo->tbsCert.extensions.raw.length;
76 
77  //Loop through the extensions
78  while(length > 0)
79  {
80  //Each extension includes an OID and a value
81  error = x509ParseExtension(data, length, &n, &extension);
82  //Any error to report?
83  if(error)
84  return error;
85 
86  //Role OID extension found?
87  if(!oidComp(extension.oid.value, extension.oid.length, MODBUS_ROLE_OID,
88  sizeof(MODBUS_ROLE_OID)))
89  {
90  //Extract the client role OID from the certificate
91  error = modbusServerParseRoleOid(connection, extension.data.value,
92  extension.data.length);
93  //Any error to report?
94  if(error)
95  return error;
96  }
97 
98  //Next extension
99  data += n;
100  length -= n;
101  }
102  }
103 
104  //Upon receipt of a certificate chain from the remote peer, the TLS end point
105  //will verify each certificate signature using the next CA certificate in the
106  //chain until it can verify the root of the chain
107  return ERROR_UNKNOWN_CA;
108 }
109 
110 
111 /**
112  * @brief Parse client role OID
113  * @param[in] connection Pointer to the client connection
114  * @param[in] data Pointer to the ASN.1 structure to parse
115  * @param[in] length Length of the ASN.1 structure
116  * @return Error code
117  **/
118 
120  const uint8_t *data, size_t length)
121 {
122  error_t error;
123  Asn1Tag tag;
124 
125  //The Role extension must be a valid UTF-8 string
126  error = asn1ReadTag(data, length, &tag);
127  //Failed to decode ASN.1 tag?
128  if(error)
129  return error;
130 
131  //Enforce encoding, class and type
132  error = asn1CheckTag(&tag, FALSE, ASN1_CLASS_UNIVERSAL,
134  //Invalid tag?
135  if(error)
136  return error;
137 
138  //Extract the client role OID from the certificate
140  {
141  //Copy client role
142  osMemcpy(connection->role, tag.value, tag.length);
143  //Properly terminate the string with a NULL character
144  connection->role[tag.length] = '\0';
145  }
146 
147  //Successful processing
148  return NO_ERROR;
149 }
150 
151 
152 /**
153  * @brief Open secure connection
154  * @param[in] context Pointer to the Modbus/TCP server context
155  * @param[in] connection Pointer to the client connection
156  * @return Error code
157  **/
158 
160  ModbusClientConnection *connection)
161 {
162  error_t error;
163 
164  //Allocate TLS context
165  connection->tlsContext = tlsInit();
166  //Failed to allocate TLS context?
167  if(connection->tlsContext == NULL)
168  return ERROR_OPEN_FAILED;
169 
170  //Select server operation mode
171  error = tlsSetConnectionEnd(connection->tlsContext,
173  //Any error to report?
174  if(error)
175  return error;
176 
177  //Bind TLS to the relevant socket
178  error = tlsSetSocket(connection->tlsContext, connection->socket);
179  //Any error to report?
180  if(error)
181  return error;
182 
183  //Set TX and RX buffer size
184  error = tlsSetBufferSize(connection->tlsContext,
186  //Any error to report?
187  if(error)
188  return error;
189 
190  //Register certificate verification callback
191  error = tlsSetCertificateVerifyCallback(connection->tlsContext,
192  modbusServerParseCertificate, connection);
193  //Any error to report?
194  if(error)
195  return error;
196 
197 #if (TLS_TICKET_SUPPORT == ENABLED)
198  //Enable session ticket mechanism
199  error = tlsEnableSessionTickets(connection->tlsContext, TRUE);
200  //Any error to report?
201  if(error)
202  return error;
203 
204  //Register ticket encryption/decryption callbacks
205  error = tlsSetTicketCallbacks(connection->tlsContext, tlsEncryptTicket,
206  tlsDecryptTicket, &context->tlsTicketContext);
207  //Any error to report?
208  if(error)
209  return error;
210 #endif
211 
212  //Invoke user-defined callback, if any
213  if(context->settings.tlsInitCallback != NULL)
214  {
215  //Perform TLS related initialization
216  error = context->settings.tlsInitCallback(connection,
217  connection->tlsContext);
218  //Any error to report?
219  if(error)
220  return error;
221  }
222 
223  //Successful processing
224  return NO_ERROR;
225 }
226 
227 
228 /**
229  * @brief Establish secure connection
230  * @param[in] connection Pointer to the client connection
231  * @return Error code
232  **/
233 
235 {
236  //Establish a TLS connection
237  return tlsConnect(connection->tlsContext);
238 }
239 
240 #endif
error_t asn1ReadTag(const uint8_t *data, size_t length, Asn1Tag *tag)
Read an ASN.1 tag from the input stream.
Definition: asn1.c:52
error_t asn1CheckTag(const Asn1Tag *tag, bool_t constructed, uint_t objClass, uint_t objType)
Enforce the type of a specified tag.
Definition: asn1.c:653
@ ASN1_TYPE_UTF8_STRING
Definition: asn1.h:79
#define ASN1_CLASS_UNIVERSAL
Definition: asn1.h:52
unsigned int uint_t
Definition: compiler_port.h:50
Debugging facilities.
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_UNKNOWN_CA
Definition: error.h:239
@ ERROR_OPEN_FAILED
Definition: error.h:75
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t data[]
Definition: ethernet.h:222
Modbus/TCP server.
#define MODBUS_SERVER_TLS_TX_BUFFER_SIZE
Definition: modbus_server.h:94
#define MODBUS_SERVER_TLS_RX_BUFFER_SIZE
#define ModbusServerContext
#define MODBUS_SERVER_MAX_ROLE_LEN
#define ModbusClientConnection
error_t modbusServerParseRoleOid(ModbusClientConnection *connection, const uint8_t *data, size_t length)
Parse client role OID.
const uint8_t MODBUS_ROLE_OID[11]
error_t modbusServerOpenSecureConnection(ModbusServerContext *context, ModbusClientConnection *connection)
Open secure connection.
error_t modbusServerParseCertificate(TlsContext *tlsContext, const X509CertInfo *certInfo, uint_t pathLen, void *param)
Parse client's certificate.
error_t modbusServerEstablishSecureConnection(ModbusClientConnection *connection)
Establish secure connection.
Modbus/TCP security layer.
TCP/IP stack core.
int_t oidComp(const uint8_t *oid1, size_t oidLen1, const uint8_t *oid2, size_t oidLen2)
Compare object identifiers.
Definition: oid.c:103
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
ASN.1 tag.
Definition: asn1.h:102
const uint8_t * value
Definition: asn1.h:107
size_t length
Definition: asn1.h:106
X.509 certificate.
Definition: x509_common.h:1064
X509TbsCertificate tbsCert
Definition: x509_common.h:1065
X.509 certificate extension.
Definition: x509_common.h:984
X509OctetString data
Definition: x509_common.h:987
X509OctetString oid
Definition: x509_common.h:985
X509OctetString raw
Definition: x509_common.h:997
const uint8_t * value
Definition: x509_common.h:647
X509Extensions extensions
Definition: x509_common.h:1055
uint8_t length
Definition: tcp.h:368
error_t tlsSetTicketCallbacks(TlsContext *context, TlsTicketEncryptCallback ticketEncryptCallback, TlsTicketDecryptCallback ticketDecryptCallback, void *param)
Set ticket encryption/decryption callbacks.
Definition: tls.c:1512
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1758
error_t tlsEnableSessionTickets(TlsContext *context, bool_t enabled)
Enable session ticket mechanism.
Definition: tls.c:1432
error_t tlsSetCertificateVerifyCallback(TlsContext *context, TlsCertVerifyCallback certVerifyCallback, void *param)
Register certificate verification callback function.
Definition: tls.c:1408
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:65
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:344
error_t tlsSetBufferSize(TlsContext *context, size_t txBufferSize, size_t rxBufferSize)
Set TLS buffer size.
Definition: tls.c:516
#define TlsContext
Definition: tls.h:36
@ TLS_CONNECTION_END_SERVER
Definition: tls.h:954
#define tlsSetSocket(context, socket)
Definition: tls.h:912
error_t tlsEncryptTicket(TlsContext *context, const uint8_t *plaintext, size_t plaintextLen, uint8_t *ciphertext, size_t *ciphertextLen, void *param)
Session ticket encryption.
Definition: tls_ticket.c:81
error_t tlsDecryptTicket(TlsContext *context, const uint8_t *ciphertext, size_t ciphertextLen, uint8_t *plaintext, size_t *plaintextLen, void *param)
Session ticket decryption.
Definition: tls_ticket.c:221
error_t x509ParseExtension(const uint8_t *data, size_t length, size_t *totalLength, X509Extension *extension)
Parse X.509 certificate extension.