Skip to content

Commit

Permalink
PKI: Make (D)TLS operation consistent across all TLS libraries
Browse files Browse the repository at this point in the history
The use of the verify_peer_cert and require_peer_cert variables in the
coap_dtls_pki_t structure was giving inconsistent results across all the
TLS libraries.  This primarily was down to the large numbers of options
available to control the TLS handshakes in OpenSSL compared to the limited
control available to mbedTLS port which followed later. require_peer_cert is
not easy to control in mbedTLS as it is an implicit configuration based on how
other, not always related, items were configured.

require_peer_cert was used by the server to control whether the client could
use anonymous certificates or not.  This is now controlled by verify_peer_cert.

require_peer_cert variable has been replaced with check_common_ca, so that
the OpenSSL functionality can continue, but enable GnuTLS / mbedTLS to
produce the same results.  This allows peers to mutually authenticate (because
the peer certs are signed by the same common CA) or not which was in effect
controlled by verify_peer_cert previously.

If check_common_ca is set, then allow_self_signed is ignored.

If is_rpk_not_cert is set, then all certificate validation is ignored.

In the examples, use of the -R option unsets check_common_ca, so disables
mutual authentication support by having a common CA.  This was needed as
mbedTLS and GnuTLS only have a single trust store for CAs.

configure.ac:

Increase the number of mbed libraries to use when checking for mbedTLS.

examples/client.c:
examples/coap-rd.c:
examples/coap-server.c:

Add in -n (unset verify_peer_cert) option. In the case of coap-server and
coap-rd, make -n refer to verify_peer_cert.
Add in TLS library capabilites in usage().

Update usage() documentation as appropriate, with some changes to fit
everything into a 80 column output.

include/coap2/coap_dtls.h:
include/coap2/net.h:

Update with variable changes, and make the coap_dtls_pki_t parameter const for
the *_context_set_pki() functions.

man/coap-client.txt.in:
man/coap-rd.txt.in:
man/coap-server.txt.in:

Update documentation to reflect the examples option usage.

man/coap_context.txt.in:
man/coap_encryption.txt.in:
man/coap_session.txt.in:

Update with the new variable name and document as appropriate.

src/coap_gnutls.c
src/coap_mbedtls.c
src/coap_notls.c
src/coap_openssl.c
coap_tinydtls.c

Update to make variable usage consistent. Update logging from LOG_WARNING to
LOG_INFO where there is an override of a PKI check failure by one of the
coap_dtls_pki_t variables.

Timing window closed for TLS where the peer does not like a certificate, sends
fatal alert and closes connection.  local then fails on writing the next
handshake - but now also reads in alert and reports on it.

src/coap_io.c:

Update logging from LOG_WARNING to LOG_INFO for EPIPE or ECONNRESET errors in
coap_socket_write().

src/net.c:

Handle the const coap_dtls_pki_t parameter in coap_context_set_pki() function.
  • Loading branch information
mrdeep1 committed Feb 18, 2021
1 parent df6615b commit a0dc6e3
Show file tree
Hide file tree
Showing 19 changed files with 858 additions and 376 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ if test "x$build_dtls" = "xyes"; then
# mbed TLS [does not have mbedtls.pc pkg-config file]
AC_CHECK_LIB(mbedtls, mbedtls_ssl_handshake,
[have_mbedtls="yes"; MbedTLS_CFLAGS="" ; MbedTLS_LIBS="-lmbedtls -lmbedcrypto -lmbedx509"],
[have_mbedtls="no"])
[have_mbedtls="no"], -lmbedx509 -lmbedcrypto)

# TinyDTLS ?
# TBD ?
Expand Down
106 changes: 72 additions & 34 deletions examples/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ static uint8_t *ca_mem = NULL; /* CA for cert checking in PEM_BUF */
static size_t cert_mem_len = 0;
static size_t key_mem_len = 0;
static size_t ca_mem_len = 0;
static int verify_peer_cert = 1; /* PKI granularity - by default set */

typedef struct ih_def_t {
char* hint_match;
Expand Down Expand Up @@ -694,21 +695,46 @@ static void
usage( const char *program, const char *version) {
const char *p;
char buffer[64];
coap_tls_version_t *tls_version = coap_get_tls_library_version();

p = strrchr( program, '/' );
if ( p )
program = ++p;

fprintf( stderr, "%s v%s -- a small CoAP implementation\n"
"Copyright (C) 2010-2021 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
"%s\n\n"
"%s\n"
, program, version, coap_string_tls_version(buffer, sizeof(buffer)));
switch (tls_version->type) {
case COAP_TLS_LIBRARY_NOTLS:
break;
case COAP_TLS_LIBRARY_TINYDTLS:
fprintf(stderr, "(DTLS and no TLS support; PSK and RPK support)\n");
break;
case COAP_TLS_LIBRARY_OPENSSL:
fprintf(stderr, "(DTLS and TLS support; PSK, PKI and no RPK support)\n");
break;
case COAP_TLS_LIBRARY_GNUTLS:
if (tls_version->version >= 0x030606)
fprintf(stderr, "(DTLS and TLS support; PSK, PKI and RPK support)\n");
else
fprintf(stderr, "(DTLS and TLS support; PSK, PKI and no RPK support)\n");
break;
case COAP_TLS_LIBRARY_MBEDTLS:
fprintf(stderr, "(DTLS and no TLS support; PSK, PKI and no RPK support)\n");
break;
default:
break;
}
fprintf(stderr, "\n"
"Usage: %s [-a addr] [-b [num,]size] [-e text] [-f file] [-l loss]\n"
"\t\t[-m method] [-o file] [-p port] [-r] [-s duration] [-t type]\n"
"\t\t[-v num] [-w] [-A type] [-B seconds] [-H hoplimit] [-K interval]\n"
"\t\t[-N] [-O num,text] [-P scheme://address[:port]] [-T token] [-U]\n"
"\t\t[[-h match_hint_file] [-k key] [-u user]]\n"
"\t\t[[-c certfile] [-j keyfile] [-C cafile] [-J pkcs11_pin]\n"
"\t\t[-M raw_pk] [-R root_cafile] [-S match_pki_sni_file]] URI\n"
"\t\t[[-c certfile] [-j keyfile] [-n] [-C cafile]\n"
"\t\t[-J pkcs11_pin] [-M raw_pk] [-R trust_casfile]\n"
"\t\t[-S match_pki_sni_file]] URI\n"
"\tURI can be an absolute URI or a URI prefixed with scheme and host\n\n"
"General Options\n"
"\t-a addr\t\tThe local interface address to use\n"
Expand Down Expand Up @@ -752,48 +778,56 @@ usage( const char *program, const char *version) {
"\t \t\tScheme is one of coap, coaps, coap+tcp and coaps+tcp\n"
"\t-T token\tInclude specified token\n"
"\t-U \t\tNever include Uri-Host or Uri-Port options\n"
,program, version, coap_string_tls_version(buffer, sizeof(buffer))
,program, wait_seconds);
fprintf( stderr,
"PSK Options (if supported by underlying (D)TLS library)\n"
"\t-h match_hint_file\n"
"\t \t\tThis is a file that contains one or more lines of Identity\n"
"\t \t\tHints to match for new user and new pre-shared key\n"
"\t \t\t(PSK) (comma separated) to be used. E.g., per line\n"
"\t \t\t hint_to_match,new_user,new_key\n"
"\t \t\tThis is a file that contains one or more lines of\n"
"\t \t\treceived Identity Hints to match to use different\n"
"\t \t\tuser identity and associated pre-shared key (PSK) (comma\n"
"\t \t\tseparated) instead of the '-k key' and '-u user'\n"
"\t \t\toptions. E.g., per line\n"
"\t \t\t hint_to_match,use_user,with_key\n"
"\t \t\tNote: -k and -u still need to be defined for the default\n"
"\t \t\tcase\n"
"\t-k key \t\tPre-shared key for the specified user\n"
"\t-u user\t\tUser identity for pre-shared key mode\n"
"\t \t\tin case there is no match\n"
"\t-k key \t\tPre-shared key for the specified user identity\n"
"\t-u user\t\tUser identity to send for pre-shared key mode\n"
"PKI Options (if supported by underlying (D)TLS library)\n"
"\tNote: If any one of '-c certfile', '-j keyfile' or '-C cafile' is in\n"
"\tPKCS11 URI naming format (pkcs11: prefix), then any remaining non\n"
"\tPKCS11 URI file definitions have to be in DER, not PEM, format.\n"
"\tOtherwise all of '-c certfile', '-j keyfile' or '-C cafile' are in\n"
"\tPEM format.\n\n"
"\t-c certfile\tPEM file or PKCS11 URI for the Certificate. The private\n"
"\t \t\tkey can be in the PEM file, or use the same PKCS11 URI.\n"
"\t \t\tIf not, the private ney is defined by '-j keyfile'\n"
"\t-c certfile\tPEM file or PKCS11 URI for the certificate. The private\n"
"\t \t\tkey can also be in the PEM file, or has the same PKCS11\n"
"\t \t\tURI. If not, the private key is defined by '-j keyfile'\n"
"\t-j keyfile\tPEM file or PKCS11 URI for the private key for the\n"
"\t \t\tnertificate in '-c certfile' if the parameter is different\n"
"\t \t\tfrom certfile in '-c certfile'\n"
"\t-C cafile\tPEM file or PKCS11 URI for the CA Certificate that was\n"
"\t \t\tused to sign the certfile. This will trigger\n"
"\t \t\tthe validation of the server certificate. If certfile is\n"
"\t \t\tself-signed (as defined by '-c certfile'), then you need\n"
"\t \t\tto have on the command line the same filename for both\n"
"\t \t\tthe certfile and cafile (as in '-c certfile -C certfile')\n"
"\t \t\tto trigger validation\n"
"\t \t\tcertificate in '-c certfile' if the parameter is\n"
"\t \t\tdifferent from certfile in '-c certfile'\n"
"\t-n \t\tDisable remote peer certificate checking\n"
"\t-C cafile\tPEM file or PKCS11 URI for the CA certificate that was\n"
"\t \t\tused to sign the server certfile. Ideally the client\n"
"\t \t\tcertificate should be signed by the same CA so that\n"
"\t \t\tmutual authentication can take place. The contents of\n"
"\t \t\tcafile are added to the trusted store of root CAs.\n"
"\t \t\tUsing the -C or -R options will trigger the\n"
"\t \t\tvalidation of the server certificate unless overridden\n"
"\t \t\tby the -n option\n"
"\t-J pkcs11_pin\tThe user pin to unlock access to the PKCS11 token\n"
"\t-M rpk_file\tRaw Public Key (RPK) PEM file or PKCS11 URI that contains\n"
"\t \t\tboth PUBLIC KEY and PRIVATE KEY or just EC PRIVATE KEY.\n"
"\t \t\t(GnuTLS and TinyDTLS support only) '-C cafile' not required\n"
"\t-R root_cafile\tPEM file containing the set of trusted root CAs that\n"
"\t \t\tare to be used to validate the server certificate.\n"
"\t \t\tThe '-C cafile' does not have to be in this list and is\n"
"\t \t\t'trusted' for the verification.\n"
"\t-M rpk_file\tRaw Public Key (RPK) PEM file or PKCS11 URI that\n"
"\t \t\tcontains both PUBLIC KEY and PRIVATE KEY or just\n"
"\t \t\tEC PRIVATE KEY. (GnuTLS and TinyDTLS(PEM) support only).\n"
"\t \t\t'-C cafile' or '-R trust_casfile' are not required\n"
"\t-R trust_casfile\tPEM file containing the set of trusted root CAs\n"
"\t \t\tthat are to be used to validate the server certificate.\n"
"\t \t\tAlternatively, this can point to a directory containing\n"
"\t \t\ta set of CA PEM files\n"
"\t \t\ta set of CA PEM files.\n"
"\t \t\tUsing '-R trust_casfile' disables common CA mutual\n"
"\t \t\tauthentication which can only be done by using\n"
"\t \t\t'-C cafile'.\n"
"\t \t\tUsing the -C or -R options will will trigger the\n"
"\t \t\tvalidation of the server certificate unless overridden\n"
"\t \t\tby the -n option\n"
);
fprintf( stderr,
"Examples:\n"
Expand Down Expand Up @@ -1477,8 +1511,8 @@ setup_pki(coap_context_t *ctx) {
* coap_context_set_pki_root_cas(), but this is used to define what
* checking actually takes place.
*/
dtls_pki.verify_peer_cert = !is_rpk_not_cert;
dtls_pki.require_peer_cert = 1;
dtls_pki.verify_peer_cert = verify_peer_cert;
dtls_pki.check_common_ca = !root_ca_file;
dtls_pki.allow_self_signed = 1;
dtls_pki.allow_expired_certs = 1;
dtls_pki.cert_chain_validation = 1;
Expand Down Expand Up @@ -1681,7 +1715,7 @@ main(int argc, char **argv) {
struct sigaction sa;
#endif

while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:o:p:rs:t:u:v:wA:B:C:H:J:K:M:NO:P:R:T:U")) != -1) {
while ((opt = getopt(argc, argv, "a:b:c:e:f:h:j:k:l:m:no:p:rs:t:u:v:wA:B:C:H:J:K:H:M:NO:P:R:T:U")) != -1) {
switch (opt) {
case 'a':
strncpy(node_str, optarg, NI_MAXHOST - 1);
Expand Down Expand Up @@ -1756,6 +1790,7 @@ main(int argc, char **argv) {
case 'M':
cert_file = optarg;
is_rpk_not_cert = 1;
verify_peer_cert = 0; /* This does not work for RPK */
break;
case 'O':
cmdline_option(optarg);
Expand Down Expand Up @@ -1800,6 +1835,9 @@ main(int argc, char **argv) {
if (!cmdline_hop_limit(optarg))
fprintf(stderr, "Hop Limit has to be > 0 and < 256\n");
break;
case 'n':
verify_peer_cert = 0;
break;
default:
usage( argv[0], LIBCOAP_PACKAGE_VERSION );
exit( 1 );
Expand Down
79 changes: 55 additions & 24 deletions examples/coap-rd.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
static char *cert_file = NULL; /* Combined certificate and private key in PEM */
static char *ca_file = NULL; /* CA for cert_file - for cert checking in PEM */
static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */
static int require_peer_cert = 1; /* By default require peer cert */
static int verify_peer_cert = 1; /* PKI granularity - by default set */
#define MAX_KEY 64 /* Maximum length of a pre-shared key in bytes. */
static uint8_t key[MAX_KEY];
static ssize_t key_length = 0;
Expand Down Expand Up @@ -581,17 +581,41 @@ static void
usage( const char *program, const char *version) {
const char *p;
char buffer[64];
coap_tls_version_t *tls_version = coap_get_tls_library_version();

p = strrchr( program, '/' );
if ( p )
program = ++p;

fprintf( stderr, "%s v%s -- CoRE Resource Directory implementation\n"
"(c) 2011-2012,2019-2021 Olaf Bergmann <bergmann@tzi.org> and others\n\n"
"%s\n\n"
"%s\n"
, program, version, coap_string_tls_version(buffer, sizeof(buffer)));
switch (tls_version->type) {
case COAP_TLS_LIBRARY_NOTLS:
break;
case COAP_TLS_LIBRARY_TINYDTLS:
fprintf(stderr, "(DTLS and no TLS support; PSK and RPK support)\n");
break;
case COAP_TLS_LIBRARY_OPENSSL:
fprintf(stderr, "(DTLS and TLS support; PSK, PKI and no RPK support)\n");
break;
case COAP_TLS_LIBRARY_GNUTLS:
if (tls_version->version >= 0x030606)
fprintf(stderr, "(DTLS and TLS support; PSK, PKI and RPK support)\n");
else
fprintf(stderr, "(DTLS and TLS support; PSK, PKI and no RPK support)\n");
break;
case COAP_TLS_LIBRARY_MBEDTLS:
fprintf(stderr, "(DTLS and no TLS support; PSK, PKI and no RPK support)\n");
break;
default:
break;
}
fprintf(stderr, "\n"
"Usage: %s [-g group] [-p port] [-v num] [-A address]\n"
"\t [[-h hint] [-k key]]\n"
"\t [[-c certfile] [-C cafile] [-n] [-R root_cafile]]\n"
"\t [[-c certfile] [-C cafile] [-n] [-R trust_casfile]]\n"
"General Options\n"
"\t-g group\tJoin the given multicast group.\n"
"\t \t\tNote: DTLS over multicast is not currently supported\n"
Expand All @@ -608,24 +632,31 @@ usage( const char *program, const char *version) {
"PKI Options (if supported by underlying (D)TLS library)\n"
"\t-c certfile\tPEM file containing both CERTIFICATE and PRIVATE KEY\n"
"\t \t\tThis argument requires (D)TLS with PKI to be available\n"
"\t-n \t\tDisable the requirement for clients to have defined\n"
"\t \t\tclient certificates\n"
"\t-C cafile\tPEM file containing the CA Certificate that was used to\n"
"\t \t\tsign the certfile. If defined, then the client will be\n"
"\t \t\tgiven this CA Certificate during the TLS set up.\n"
"\t \t\tFurthermore, this will trigger the validation of the\n"
"\t \t\tclient certificate. If certfile is self-signed (as\n"
"\t \t\tdefined by '-c certfile'), then you need to have on the\n"
"\t \t\tcommand line the same filename for both the certfile and\n"
"\t \t\tcafile (as in '-c certfile -C certfile') to trigger\n"
"\t \t\tvalidation\n"
"\t-R root_cafile\tPEM file containing the set of trusted root CAs that\n"
"\t \t\tare to be used to validate the client certificate.\n"
"\t \t\tThe '-C cafile' does not have to be in this list and is\n"
"\t \t\t'trusted' for the verification.\n"
"\t-n \t\tDisable remote peer certificate checking. This gives\n"
"\t \t\tclients the ability to use PKI, but without any defined\n"
"\t \t\tcertificates\n"
"\t-C cafile\tPEM file that contains a list of one or\n"
"\t \t\tmore CAs that are to be passed to the client for the\n"
"\t \t\tclient to determine what client certificate to use.\n"
"\t \t\tNormally, this list of CAs would be the root CA and and\n"
"\t \t\tany intermediate CAs. Ideally the server certificate\n"
"\t \t\tshould be signed by the same CA so that mutual\n"
"\t \t\tauthentication can take place. The contents of cafile\n"
"\t \t\tare added to the trusted store of root CAs.\n"
"\t \t\tUsing the -C or -R options will will trigger the\n"
"\t \t\tvalidation of the client certificate unless overridden\n"
"\t \t\tby the -n option\n"
"\t-R trust_casfile\tPEM file containing the set of trusted root CAs\n"
"\t \t\tthat are to be used to validate the client certificate.\n"
"\t \t\tAlternatively, this can point to a directory containing\n"
"\t \t\ta set of CA PEM files\n",
program, version, coap_string_tls_version(buffer, sizeof(buffer)),
"\t \t\ta set of CA PEM files.\n"
"\t \t\tUsing '-R trust_casfile' disables common CA mutual\n"
"\t \t\tauthentication which can only be done by using\n"
"\t \t\t'-C cafile'.\n"
"\t \t\tUsing the -C or -R options will will trigger the\n"
"\t \t\tvalidation of the client certificate unless overridden\n"
"\t \t\tby the -n option\n"
,
program);
}

Expand All @@ -641,14 +672,14 @@ fill_keystore(coap_context_t *ctx) {
coap_dtls_pki_t dtls_pki;
memset (&dtls_pki, 0, sizeof(dtls_pki));
dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
if (ca_file) {
if (ca_file || root_ca_file) {
/*
* Add in additional certificate checking.
* This list of enabled can be tuned for the specific
* requirements - see 'man coap_encryption'.
*/
dtls_pki.verify_peer_cert = 1;
dtls_pki.require_peer_cert = require_peer_cert;
dtls_pki.verify_peer_cert = verify_peer_cert;
dtls_pki.check_common_ca = !root_ca_file;
dtls_pki.allow_self_signed = 1;
dtls_pki.allow_expired_certs = 1;
dtls_pki.cert_chain_validation = 1;
Expand Down Expand Up @@ -813,7 +844,7 @@ main(int argc, char **argv) {
key_defined = 1;
break;
case 'n':
require_peer_cert = 0;
verify_peer_cert = 0;
break;
case 'R' :
root_ca_file = optarg;
Expand Down
Loading

0 comments on commit a0dc6e3

Please sign in to comment.