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-2019 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 1.9.2
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 X509CertificateInfo *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->extensions.rawData;
75  length = certInfo->extensions.rawDataLen;
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, extension.oidLen, MODBUS_ROLE_OID,
88  sizeof(MODBUS_ROLE_OID)))
89  {
90  //Extract the client role OID from the certificate
91  error = modbusServerParseRoleOid(connection, extension.value,
92  extension.valueLen);
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  memcpy(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 = tlsSetTicketCallbacks(connection->tlsContext, tlsEncryptTicket,
200  tlsDecryptTicket, &context->tlsTicketContext);
201  //Any error to report?
202  if(error)
203  return error;
204 #endif
205 
206  //Invoke user-defined callback, if any
207  if(context->settings.tlsInitCallback != NULL)
208  {
209  //Perform TLS related initialization
210  error = context->settings.tlsInitCallback(connection,
211  connection->tlsContext);
212  //Any error to report?
213  if(error)
214  return error;
215  }
216 
217  //Successful processing
218  return NO_ERROR;
219 }
220 
221 
222 /**
223  * @brief Establish secure connection
224  * @param[in] connection Pointer to the client connection
225  * @return Error code
226  **/
227 
229 {
230  //Establish a TLS connection
231  return tlsConnect(connection->tlsContext);
232 }
233 
234 #endif
error_t tlsSetBufferSize(TlsContext *context, size_t txBufferSize, size_t rxBufferSize)
Set TLS buffer size.
Definition: tls.c:481
Modbus/TCP server.
Modbus/TCP security layer.
size_t rawDataLen
Definition: x509_common.h:713
TCP/IP stack core.
Debugging facilities.
error_t modbusServerOpenSecureConnection(ModbusServerContext *context, ModbusClientConnection *connection)
Open secure connection.
X.509 certificate extension.
Definition: x509_common.h:696
error_t tlsConnect(TlsContext *context)
Initiate the TLS handshake.
Definition: tls.c:1560
error_t modbusServerParseRoleOid(ModbusClientConnection *connection, const uint8_t *data, size_t length)
Parse client role OID.
error_t tlsSetConnectionEnd(TlsContext *context, TlsConnectionEnd entity)
Set operation mode (client or server)
Definition: tls.c:312
error_t tlsSetCertificateVerifyCallback(TlsContext *context, TlsCertVerifyCallback certVerifyCallback, void *param)
Set certificate verification callback.
Definition: tls.c:1235
#define MODBUS_SERVER_MAX_ROLE_LEN
size_t valueLen
Definition: x509_common.h:702
const uint8_t * value
Definition: x509_common.h:701
#define ModbusClientConnection
ASN.1 tag.
Definition: asn1.h:96
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:488
error_t tlsSetTicketCallbacks(TlsContext *context, TlsTicketEncryptCallback ticketEncryptCallback, TlsTicketDecryptCallback ticketDecryptCallback, void *param)
Set ticket encryption/decryption callbacks.
Definition: tls.c:1314
const uint8_t MODBUS_ROLE_OID[11]
#define tlsSetSocket(context, socket)
Definition: tls.h:823
error_t x509ParseExtension(const uint8_t *data, size_t length, size_t *totalLength, X509Extension *extension)
Parse X.509 certificate extension.
error_t modbusServerEstablishSecureConnection(ModbusClientConnection *connection)
Establish secure connection.
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
X509Extensions extensions
Definition: x509_common.h:776
size_t length
Definition: asn1.h:101
int_t oidComp(const uint8_t *oid1, size_t oidLen1, const uint8_t *oid2, size_t oidLen2)
Compare object identifiers.
Definition: oid.c:101
Success.
Definition: error.h:44
#define MODBUS_SERVER_TLS_RX_BUFFER_SIZE
Definition: modbus_server.h:94
#define ModbusServerContext
error_t
Error codes.
Definition: error.h:42
unsigned int uint_t
Definition: compiler_port.h:45
uint8_t data[]
Definition: dtls_misc.h:169
X.509 certificate.
Definition: x509_common.h:766
#define MODBUS_SERVER_TLS_TX_BUFFER_SIZE
Definition: modbus_server.h:87
#define ASN1_CLASS_UNIVERSAL
Definition: asn1.h:47
TlsContext * tlsInit(void)
TLS context initialization.
Definition: tls.c:65
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:211
const uint8_t * oid
Definition: x509_common.h:698
uint8_t length
Definition: dtls_misc.h:142
uint8_t n
const uint8_t * rawData
Definition: x509_common.h:712
#define FALSE
Definition: os_port.h:46
#define TlsContext
Definition: tls.h:36
const uint8_t * value
Definition: asn1.h:102
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:82
error_t modbusServerParseCertificate(TlsContext *tlsContext, const X509CertificateInfo *certInfo, uint_t pathLen, void *param)
Parse client's certificate.