From a0dc6e3663a0913dd4450fb4706b4ea951a2edc5 Mon Sep 17 00:00:00 2001 From: Jon Shallow Date: Tue, 27 Oct 2020 13:24:19 +0000 Subject: [PATCH] PKI: Make (D)TLS operation consistent across all TLS libraries 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. --- configure.ac | 2 +- examples/client.c | 106 ++++++++----- examples/coap-rd.c | 79 +++++++--- examples/coap-server.c | 153 ++++++++++++------- include/coap2/coap_dtls.h | 10 +- include/coap2/net.h | 2 +- man/coap-client.txt.in | 57 +++---- man/coap-rd.txt.in | 39 ++--- man/coap-server.txt.in | 87 ++++++----- man/coap_context.txt.in | 11 +- man/coap_encryption.txt.in | 30 +++- man/coap_session.txt.in | 2 +- src/coap_gnutls.c | 296 +++++++++++++++++++++++++++++-------- src/coap_io.c | 10 +- src/coap_mbedtls.c | 166 ++++++++++++++++----- src/coap_notls.c | 4 +- src/coap_openssl.c | 171 +++++++++++++-------- src/coap_tinydtls.c | 5 +- src/net.c | 4 +- 19 files changed, 858 insertions(+), 376 deletions(-) diff --git a/configure.ac b/configure.ac index a0c9e77f3d..6cefe08f79 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ? diff --git a/examples/client.c b/examples/client.c index 20b138c062..caf2a9aa60 100644 --- a/examples/client.c +++ b/examples/client.c @@ -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; @@ -694,6 +695,7 @@ 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 ) @@ -701,14 +703,38 @@ usage( const char *program, const char *version) { fprintf( stderr, "%s v%s -- a small CoAP implementation\n" "Copyright (C) 2010-2021 Olaf Bergmann 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" @@ -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" @@ -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; @@ -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); @@ -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); @@ -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 ); diff --git a/examples/coap-rd.c b/examples/coap-rd.c index 4f862bba51..3e54333022 100644 --- a/examples/coap-rd.c +++ b/examples/coap-rd.c @@ -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; @@ -581,6 +581,7 @@ 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 ) @@ -588,10 +589,33 @@ usage( const char *program, const char *version) { fprintf( stderr, "%s v%s -- CoRE Resource Directory implementation\n" "(c) 2011-2012,2019-2021 Olaf Bergmann 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" @@ -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); } @@ -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; @@ -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; diff --git a/examples/coap-server.c b/examples/coap-server.c index 57c32a78bc..5920d39316 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -98,7 +98,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 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 = NULL; static ssize_t key_length = 0; @@ -1873,7 +1873,8 @@ setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *client_sni) { * 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.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; @@ -1887,7 +1888,6 @@ setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *client_sni) { verify_pki_sni_callback : NULL; dtls_pki.sni_call_back_arg = NULL; } - dtls_pki.require_peer_cert = require_peer_cert; dtls_pki.is_rpk_not_cert = is_rpk_not_cert; if (role == COAP_DTLS_ROLE_CLIENT) { @@ -1977,6 +1977,7 @@ 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 ) @@ -1984,13 +1985,37 @@ usage( const char *program, const char *version) { fprintf( stderr, "%s v%s -- a small CoAP implementation\n" "(c) 2010,2011,2015-2021 Olaf Bergmann 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 [-d max] [-g group] [-l loss] [-p port] [-v num]\n" "\t\t[-A address] [-N] [-P scheme://address[:port],name1[,name2..]]\n" "\t\t[[-h hint] [-i match_identity_file] [-k key]\n" "\t\t[-s match_psk_sni_file] [-u user]]\n" - "\t\t[[-c certfile] [-j keyfile] [-m] [-n] [-C cafile] [-J pkcs11_pin]\n" - "\t\t[-M rpk_file] [-R root_cafile] [-S match_pki_sni_file]]\n" + "\t\t[[-c certfile] [-j keyfile] [-m] [-n] [-C cafile]\n" + "\t\t[-J pkcs11_pin] [-M rpk_file] [-R trust_casfile]\n" + "\t\t[-S match_pki_sni_file]]\n" "General Options\n" "\t-d max \t\tAllow dynamic creation of up to a total of max\n" "\t \t\tresources. If max is reached, a 4.06 code is returned\n" @@ -2006,13 +2031,13 @@ usage( const char *program, const char *version) { "\t \t\tenabled, then the coap-server will also listen on\n" "\t \t\t 'port'+1 for DTLS and TLS. The default port is 5683\n" "\t-v num \t\tVerbosity level (default 3, maximum is 9). Above 7,\n" - "\t \t\tthere is increased verbosity in GnuTLS and OpenSSL logging\n" + "\t \t\tthere is increased verbosity in GnuTLS and OpenSSL\n" + "\t \t\tlogging\n" "\t-A address\tInterface address to bind to\n" "\t-N \t\tMake \"observe\" responses NON-confirmable. Even if set\n" "\t \t\tevery fifth response will still be sent as a confirmable\n" "\t \t\tresponse (RFC 7641 requirement)\n" - , program, version, coap_string_tls_version(buffer, sizeof(buffer)), - program); + , program); fprintf( stderr, "\t-P scheme://address[:port],name1[,name2[,name3..]]\tScheme, address,\n" "\t \t\toptional port of how to connect to the next proxy server\n" @@ -2025,29 +2050,35 @@ usage( const char *program, const char *version) { "\t \t\twill be a direct connection.\n" "\t \t\tScheme is one of coap, coaps, coap+tcp and coaps+tcp\n" "PSK Options (if supported by underlying (D)TLS library)\n" - "\t-h hint\t\tIdentity Hint. Default is CoAP. Zero length is no hint\n" + "\t-h hint\t\tIdentity Hint to send. Default is CoAP. Zero length is\n" + "\t \t\tno hint\n" "\t-i match_identity_file\n" - "\t \t\tThis option denotes a file that contains one or more lines\n" - "\t \t\tof client Hints and (user) Identities to match for a new\n" - "\t \t\tPre-Shared Key (PSK) (comma separated) to be used. E.g.,\n" - "\t \t\tper line\n" - "\t \t\t hint_to_match,identity_to_match,new_key\n" + "\t \t\tThis is a file that contains one or more lines of\n" + "\t \t\tIdentity Hints and (user) Identities to match for\n" + "\t \t\ta different new Pre-Shared Key (PSK) (comma separated)\n" + "\t \t\tto be used. E.g., per line\n" + "\t \t\t hint_to_match,identity_to_match,use_key\n" "\t \t\tNote: -k still needs to be defined for the default case\n" + "\t \t\tNote: A match using the -s option may mean that the\n" + "\t \t\tcurrent Identity Hint is different to that defined by -h\n" "\t-k key \t\tPre-Shared Key. This argument requires (D)TLS with PSK\n" "\t \t\tto be available. This cannot be empty if defined.\n" - "\t \t\tNote that both -c and -k need to be defined\n" - "\t \t\tfor both PSK and PKI to be concurrently supported\n" + "\t \t\tNote that both -c and -k need to be defined for both\n" + "\t \t\tPSK and PKI to be concurrently supported\n" "\t-s match_psk_sni_file\n" - "\t \t\tThis is a file that contains one or more lines of Subject\n" - "\t \t\tName Identifiers (SNI) to match for new Identity Hint and\n" - "\t \t\tnew Pre-Shared Key (PSK) (comma separated) to be used.\n" - "\t \t\tE.g., per line\n" - "\t \t\t sni_to_match,new_hint,new_key\n" + "\t \t\tThis is a file that contains one or more lines of\n" + "\t \t\treceived Subject Name Identifier (SNI) to match to use\n" + "\t \t\ta different Identity Hint and associated Pre-Shared Key\n" + "\t \t\t(PSK) (comma separated) instead of the '-h hint' and\n" + "\t \t\t'-k key' options. E.g., per line\n" + "\t \t\t sni_to_match,use_hint,with_key\n" "\t \t\tNote: -k still needs to be defined for the default case\n" - "\t \t\tNote: the new Pre-Shared Key will get updated if there is\n" - "\t \t\talso a -i match\n" - "\t-u user\t\tUser identity for pre-shared key mode (only used if option P\n" - "\t \t\t is set)\n" + "\t \t\tif there is not a match\n" + "\t \t\tNote: The associated Pre-Shared Key will get updated if\n" + "\t \t\tthere is also a -i match. The update checking order is\n" + "\t \t\t-s followed by -i\n" + "\t-u user\t\tUser identity for pre-shared key mode (only used if\n" + "\t \t\toption -P is set)\n" ); fprintf(stderr, "PKI Options (if supported by underlying (D)TLS library)\n" @@ -2057,43 +2088,52 @@ usage( const char *program, const char *version) { "\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 key is defined by '-j keyfile'\n" - "\t \t\tNote that both -c and -k need to be defined\n" - "\t \t\tfor both PSK and PKI to be concurrently supported\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 \t\tNote that both -c and -k need to be defined for both\n" + "\t \t\tPSK and PKI to be concurrently supported\n" "\t-j keyfile\tPEM file or PKCS11 URI for the private key for the\n" - "\t \t\tcertificate in '-c certfile' if the parameter is different\n" - "\t \t\tfrom certfile in '-c certfile'\n" + "\t \t\tcertificate in '-c certfile' if the parameter is\n" + "\t \t\tdifferent from certfile in '-c certfile'\n" "\t-m \t\tUse COAP_PKI_KEY_PEM_BUF instead of COAP_PKI_KEY_PEM i/f\n" "\t \t\tby reading into memory the Cert / CA file (for testing)\n" - "\t-n \t\tDisable the requirement for clients to have defined\n" - "\t \t\tclient certificates\n" - "\t-C cafile\tPEM file or PKCS11 URI for the CA certificate that was\n" - "\t \t\tused to sign the certfile. If defined, then the client\n" - "\t \t\twill be given 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-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 or PKCS11 URI 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-J pkcs11_pin\tThe user pin to unlock access to the PKCS11 token\n" - "\t-M rpk_file\tRaw Public Key (RPK) PEM file that contains both\n" - "\t \t\tPUBLIC 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 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-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 client 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 client certificate unless overridden\n" + "\t \t\tby the -n option\n" "\t-S match_pki_sni_file\n" - "\t \t\tThis option denotes a file that contains one or more lines\n" - "\t \t\tof Subject Name Identifier (SNI) to match for new Cert\n" - "\t \t\tfile and new CA file (comma separated) to be used.\n" + "\t \t\tThis option denotes a file that contains one or more\n" + "\t \t\tlines of Subject Name Identifier (SNI) to match for new\n" + "\t \t\tCert file and new CA file (comma separated) to be used.\n" "\t \t\tE.g., per line\n" "\t \t\t sni_to_match,new_cert_file,new_ca_file\n" - "\t \t\tNote: -c and -C still needs to be defined for the default case\n" + "\t \t\tNote: -c and -C still need to be defined for the default\n" + "\t \t\tcase\n" ); } @@ -2486,9 +2526,10 @@ 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 'n': - require_peer_cert = 0; + verify_peer_cert = 0; break; case 'N': resource_flags = COAP_RESOURCE_FLAGS_NOTIFY_NON; diff --git a/include/coap2/coap_dtls.h b/include/coap2/coap_dtls.h index a2539436ed..dda8990aa1 100644 --- a/include/coap2/coap_dtls.h +++ b/include/coap2/coap_dtls.h @@ -248,8 +248,10 @@ typedef struct coap_dtls_pki_t { /* Options to enable different TLS functionality in libcoap */ uint8_t verify_peer_cert; /**< 1 if peer cert is to be verified */ - uint8_t require_peer_cert; /**< 1 if peer cert is required */ - uint8_t allow_self_signed; /**< 1 if self signed certs are allowed */ + uint8_t check_common_ca; /**< 1 if peer cert is to be signed by + * the same CA as the local cert */ + uint8_t allow_self_signed; /**< 1 if self-signed certs are allowed. + * Ignored if check_common_ca set */ uint8_t allow_expired_certs; /**< 1 if expired certs are allowed */ uint8_t cert_chain_validation; /**< 1 if to check cert_chain_verify_depth */ uint8_t cert_chain_verify_depth; /**< recommended depth is 3 */ @@ -536,8 +538,8 @@ coap_dtls_context_set_cpsk(struct coap_context_t *coap_context, int coap_dtls_context_set_pki(struct coap_context_t *coap_context, - coap_dtls_pki_t *setup_data, - coap_dtls_role_t role); + const coap_dtls_pki_t *setup_data, + const coap_dtls_role_t role); /** * Set the dtls context's default Root CA information for a client or server. diff --git a/include/coap2/net.h b/include/coap2/net.h index b45b6b19bd..31834256c2 100644 --- a/include/coap2/net.h +++ b/include/coap2/net.h @@ -339,7 +339,7 @@ int coap_context_set_psk2(coap_context_t *context, */ int coap_context_set_pki(coap_context_t *context, - coap_dtls_pki_t *setup_data); + const coap_dtls_pki_t *setup_data); /** * Set the context's default Root CA information for a client or server. diff --git a/man/coap-client.txt.in b/man/coap-client.txt.in index 7571541d83..848c62ed89 100644 --- a/man/coap-client.txt.in +++ b/man/coap-client.txt.in @@ -20,8 +20,8 @@ SYNOPSIS [*-H* hoplimit] [*-K* interval] [*-N*] [*-O* num,text] [*-P* scheme://addr[:port]] [*-T* token] [*-U*] [[*-h* match_hint_file] [*-k* key] [*-u* user]] - [[*-c* certfile] [*-j* keyfile] [*-C* cafile] [*-J* pkcs11_pin] - [*-M* rpk_file] [*-R* root_cafile]] URI + [[*-c* certfile] [*-j* keyfile] [-n] [*-C* cafile] + [*-J* pkcs11_pin] [*-M* rpk_file] [*-R* trust_casfile]] URI DESCRIPTION ----------- @@ -148,19 +148,20 @@ OPTIONS - PSK (If supported by underlying (D)TLS library) *-h* match_hint_file:: - This option denotes a file that contains one or more lines of Identity - Hints to match for new user and new pre-shared key (PSK) (comma separated) - to be used. - E.g., entry per line + - hint_to_match,new_user,new_key + + This is a file that contains one or more lines of received Identity Hints + to match to use different user identity and associated pre-shared key (PSK) + (comma separated) instead of the *-k key* and *-u user* options. E.g., per + line + + hint_to_match,use_user,with_key + A line that starts with # is treated as a comment. + - Note: *-k key* and *-u user* still need to be defined for the default case. + Note: *-k key* and *-u user* still need to be defined for the default case in + case there is no match. *-k* key:: - Pre-shared key for the specified user (*-u* option also required). + Pre-shared key for the specified user identity (*-u* option also required). *-u* user:: - User identity for pre-shared key mode (*-k* option also required). + User identity to send for pre-shared key mode (*-k* option also required). OPTIONS - PKI ------------- @@ -172,35 +173,41 @@ definitions have to be in DER, not PEM, format. Otherwise all of *certfile*, *keyfile* or *cafile* are in PEM format. *-c* certfile:: - PEM file or PKCS11 URI for the certificate. The private key can be in the - PEM file, or use the same PKCS11 URI. If not, the private key is defined + PEM file or PKCS11 URI for the certificate. The private key can also be in + the PEM file, or has the same PKCS11 URI. If not, the private key is defined by *-j keyfile*. *-j* keyfile:: PEM file or PKCS11 URI for the private key for the certificate in *-c certfile* if the parameter is different from certfile in *-c certfile*. +*-n* :: + Disable remote peer certificate checking. + *-C* cafile:: - PEM file or PKCS11 URI for the CA certificate that was used to sign the - certfile defined using *-c certfile*. - This will trigger the validation of the server certificate. - If certfile is self-signed (as defined by *-c certfile*), then you need to - have on the command line the same filename for both the certfile and cafile - (as in *-c certfile -C certfile*) to trigger validation. +PEM file or PKCS11 URI for the CA certificate that was used to sign the server + certfile. Ideally the client certificate should be signed by the same CA so + that mutual authentication can take place. The contents of cafile are added + to the trusted store of root CAs. Using the *-C* or *-R* options will trigger + the validation of the server certificate unless overridden by the *-n* option. *-J* pkcs11_pin:: The user pin to unlock access to the PKCS11 token. *-M* rpk_file:: - Raw Public Key (RPK) PEM file that contains both PUBLIC KEY and PRIVATE KEY - or just EC PRIVATE KEY. - (GnuTLS and TinyDTLS support only) '-C cafile' not required. + Raw Public Key (RPK) PEM file or PKCS11 URI that contains both PUBLIC KEY + and PRIVATE KEY or just EC PRIVATE KEY. (GnuTLS and TinyDTLS(PEM) support + only). *-C cafile* or *-R trust_casfile* are not required. -*-R* root_cafile:: +*-R* trust_casfile:: PEM file containing the set of trusted root CAs that are to be used to - validate the server certificate. The *-C cafile* does not have to be in - this list and is "trusted" for the verification. - Alternatively, this can point to a directory containing a set of CA PEM files. + validate the server certificate. Alternatively, this can point to a + directory containing a set of CA PEM files. The *-C cafile* CA does not have + to be in this list and is trusted for the validation. Using + *-R trust_casfile* disables common CA mutual authentication which can only + be done by using *-C cafile*. Using the *-C* or *-R* options will will + trigger the validation of the server certificate unless overridden by the + *-n* option. EXAMPLES -------- diff --git a/man/coap-rd.txt.in b/man/coap-rd.txt.in index 1cd4b1edbb..01bb0be499 100644 --- a/man/coap-rd.txt.in +++ b/man/coap-rd.txt.in @@ -16,7 +16,7 @@ SYNOPSIS -------- *coap-rd* [*-g* group] [*-p* port] [*-v* num] [*-A* address] [[*-h* hint] [*-k* key]] - [[*-c* certfile] [*-n*] [*-C* cafile] [*-R* root_cafile]] + [[*-c* certfile] [*-n*] [*-C* cafile] [*-R* trusted_casfile]] DESCRIPTION ----------- @@ -47,8 +47,7 @@ OPTIONS - PSK (If supported by underlying (D)TLS library) *-h* hint:: - Identity hint to use for inbound connections. The default is 'CoAP'. - This cannot be empty if defined. + Identity Hint to send. Default is *CoAP*. Zero length is no hint. *-k* key:: Pre-shared key to use for inbound connections. This cannot be empty if @@ -63,26 +62,32 @@ OPTIONS - PKI *-c* certfile:: Use the specified PEM file which contains the CERTIFICATE and PRIVATE KEY information. - *Note:* if *-k key* is defined, you need to define *-c cafile* as well to + Note: if *-k key* is defined, you need to define *-c certfile* as well to have the server support both PSK and PKI. *-n* :: - Disable the requirement for clients to have defined client certificates. + Disable remote peer certificate checking. This gives clients the ability to + use PKI, but without any defined certificates. *-C* cafile:: - PEM file containing the CA Certificate that was used to sign the certfile - defined using *-c certfile*. - If defined, then the client will be given this CA Certificate during the TLS - set up. Furthermore, this will trigger the validation of the client - certificate. If certfile is self-signed (as defined by *-c certfile*), then - you need to have on the command line the same filename for both the certfile - and cafile (as in *-c certfile -C certfile*) to trigger validation. - -*-R* root_cafile:: + PEM file that contains a list of one or more CAs that are to + be passed to the client for the client to determine what client certificate + to use. Normally, this list of CAs would be the root CA and and any + intermediate CAs. Ideally the server certificate should be signed by the + same CA so that mutual authentication can take place. The contents of + *cafile* are added to the trusted store of root CAs. Using the *-C* or *-R* + options will will trigger the validation of the client certificate unless + overridden by the *-n* option. + +*-R* trust_casfile:: PEM file containing the set of trusted root CAs that are to be used to - validate the server certificate. The *-C cafile* does not have to be in - this list and is "trusted" for the verification. - Alternatively, this can point to a directory containing a set of CA PEM files. + validate the client certificate. Alternatively, this can point to a + directory containing a set of CA PEM files. The *-C cafile* CA does not have + to be in this list and is trusted for the validation. Using + *-R trust_casfile* disables common CA mutual authentication which can only + be done by using *-C cafile*. Using the *-C* or *-R* options will will + trigger the validation of the server certificate unless overridden by the + *-n* option. EXAMPLES -------- diff --git a/man/coap-server.txt.in b/man/coap-server.txt.in index 3cd638c1b3..3f0922127b 100644 --- a/man/coap-server.txt.in +++ b/man/coap-server.txt.in @@ -19,7 +19,7 @@ SYNOPSIS [[*-h* hint] [*-i* match_identity_file] [*-k* key] [*-s* match_psk_sni_file] [*-u* user]] [[*-c* certfile] [*-j* keyfile] [*-n*] [*-C* cafile] - [*-J* pkcs11_pin] [*-M* rpk_file] [*-R* root_cafile] + [*-J* pkcs11_pin] [*-M* rpk_file] [*-R* trust_casfile] [*-S* match_pki_sni_file]] DESCRIPTION @@ -79,17 +79,17 @@ OPTIONS - PSK (If supported by underlying (D)TLS library) *-h* hint:: - Identity hint to use for inbound connections. The default is 'CoAP'. - This cannot be empty if defined. + Identity Hint to send. Default is *CoAP*. Zero length is no hint. *-i* match_identiity_file:: - This option denotes a file that contains one or more lines of client Hints - and (user) Identities to match for a new Pre-Shared key (PSK) - (comma separated) to be used. - E.g., entry per line + - hint_to_match,identity_to_match,new_key + + This is a file that contains one or more lines of Identity Hints and (user) + Identities to match for a different new Pre-Shared Key (PSK) (comma + separated) to be used. E.g., per line + + hint_to_match,identity_to_match,use_key + A line that starts with # is treated as a comment. + - Note: -k still needs to be defined for the default case + + Note: *-k* still needs to be defined for the default case. + + Note: A match using the *-s* option may mean that the current Identity Hint + is different to that defined by *-h*. *-k* key:: Pre-shared key to use for inbound connections. This cannot be empty if @@ -98,17 +98,18 @@ OPTIONS - PSK have the server support both PSK and PKI. *-s* match_psk_sni_file:: - This option denotes a file that contains one or more lines of Subject Name - Identifier (SNI) to match for new Identity Hint and new Pre-Shared Key - (PSK) (comma separated) to be used. - E.g., entry per line + - sni_to_match,new_hint,new_key + - A line that starts with # is treated as a comment. + - Note: -k still needs to be defined for the default case + - Note: the new Pre-Shared Key will get updated if there is also a -i match + This is a file that contains one or more lines of received Subject Name + Identifier (SNI) to match to use a different Identity Hint and associated + Pre-Shared Key (PSK) (comma separated) instead of the *-h hint* and + *-k key* options. E.g., per line + + sni_to_match,use_hint,with_key + + Note: *-k key* still needs to be defined for the default case if there is + not a match. + + Note: The associated Pre-Shared Key will get updated if there is also a *-i* + match. The update checking order is *-s* followed by *-i*. *-u* user :: - User identity for pre-shared key mode (only used if option P is set). + User identity for pre-shared key mode (only used if option *-P* is set). OPTIONS - PKI ------------- @@ -120,41 +121,47 @@ definitions have to be in DER, not PEM, format. Otherwise all of *certfile*, *keyfile* or *cafile* are in PEM format. *-c* certfile:: - PEM file or PKCS11 URI for the certificate. The private key can be in - the PEM file, or use the same PKCS11 URI. If not, the private key is defined - by *-j keyfile*. + - Note: if *-k key* is defined, you need to define *-c certfile* as well to - have the server support both PSK and PKI. + PEM file or PKCS11 URI for the certificate. The private key can also be in + the PEM file, or has the same PKCS11 URI. If not, the private key is defined + by *-j keyfile*. + + Note: if *-k key* is defined, you need to define *-c certfile* as well to + have the server support both PSK and PKI. *-j* keyfile:: - PEM file or PKCS11 URI for the private key for the certificate in *-c - certfile* if the parameter is different from certfile in *-c certfile*. + PEM file or PKCS11 URI for the private key for the certificate in *-c + certfile* if the parameter is different from certfile in *-c certfile*. *-n* :: - Disable the requirement for clients to have defined client certificates. + Disable remote peer certificate checking. This gives clients the ability to + use PKI, but without any defined certificates. *-C* cafile:: - PEM file or PKCS11 URI for the CA certificate that was used to sign the - certfile. If defined, then the client will be given this CA certificate - during the TLS set up. Furthermore, this will trigger the validation of the - client certificate. If certfile is self-signed (as defined by *-c - certfile*), then you need to have on the command line the same filename for - both the certfile and cafile (as in *-c certfile -C certfile*) to trigger - validation. + PEM file or PKCS11 URI that contains a list of one or more CAs that are to + be passed to the client for the client to determine what client certificate + to use. Normally, this list of CAs would be the root CA and and any + intermediate CAs. Ideally the server certificate should be signed by the + same CA so that mutual authentication can take place. The contents of + *cafile* are added to the trusted store of root CAs. Using the *-C* or *-R* + options will will trigger the validation of the client certificate unless + overridden by the *-n* option. *-J* pkcs11_pin:: The user pin to unlock access to the PKCS11 token. *-M*:: - Raw Public Key (RPK) PEM file that contains both PUBLIC KEY and PRIVATE KEY - or just EC PRIVATE KEY. - (GnuTLS and TinyDTLS support only) '-C cafile' not required. + Raw Public Key (RPK) PEM file or PKCS11 URI that contains both PUBLIC KEY + and PRIVATE KEY or just EC PRIVATE KEY. (GnuTLS and TinyDTLS(PEM) support + only). *-C cafile* or *-R trust_casfile* are not required. -*-R* root_cafile:: +*-R* trust_casfile:: PEM file containing the set of trusted root CAs that are to be used to - validate the server certificate. The *-C cafile* does not have to be in - this list and is "trusted" for the verification. - Alternatively, this can point to a directory containing a set of CA PEM files. + validate the client certificate. Alternatively, this can point to a + directory containing a set of CA PEM files. The *-C cafile* CA does not have + to be in this list and is trusted for the validation. Using + *-R trust_casfile* disables common CA mutual authentication which can only + be done by using *-C cafile*. Using the *-C* or *-R* options will will + trigger the validation of the server certificate unless overridden by the + *-n* option. *-S* match_pki_sni_file:: This option denotes a file that contains one or more lines of Subject Name diff --git a/man/coap_context.txt.in b/man/coap_context.txt.in index e505f88036..43d7eda4a0 100644 --- a/man/coap_context.txt.in +++ b/man/coap_context.txt.in @@ -30,7 +30,7 @@ SYNOPSIS *void coap_free_context(coap_context_t *_context_);* *int coap_context_set_pki(coap_context_t *_context_, -coap_dtls_pki_t *_setup_data_);* +const coap_dtls_pki_t *_setup_data_);* *int coap_context_set_pki_root_cas(coap_context_t *_context_, const char *_ca_file_, const char *_ca_dir_);* @@ -124,7 +124,12 @@ root CAs to be used instead of the default set of root CAs provided as a part of the TLS library. _ca_file_ points to a PEM encoded file containing the list of CAs. _ca_file can be NULL. _ca_dir_ points to a directory containing a set of PEM encoded files containing rootCAs. _ca_dir_ can be -NULL. One or both of _ca_file_ and _ca_dir_ must be set. +NULL. One or both of _ca_file_ and _ca_dir_ must be set. + +*NOTE:* Some TLS libraries send the full list of CAs added by this function +during the (D)TLS session setup handshakes. To stop this, either provide a +single CA using the _ca_file_ definition in _pki_key_ in _setup_data_ variable +when calling *coap_context_set_pki*(), or set _check_common_ca to 0 in +_setup_data_ variable. See *coap_encryption*(3). The *coap_context_set_psk2*() function is used to configure the TLS context using the _setup_data_ variables as defined in the @@ -294,7 +299,7 @@ setup_server_context_pki (const char *public_cert_file, /* see coap_encryption(3) */ dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION; dtls_pki.verify_peer_cert = 1; - dtls_pki.require_peer_cert = 1; + dtls_pki.check_common_ca = 1; dtls_pki.allow_self_signed = 1; dtls_pki.allow_expired_certs = 1; dtls_pki.cert_chain_validation = 1; diff --git a/man/coap_encryption.txt.in b/man/coap_encryption.txt.in index 1205bc601b..94828f3916 100644 --- a/man/coap_encryption.txt.in +++ b/man/coap_encryption.txt.in @@ -417,8 +417,11 @@ typedef struct coap_dtls_pki_t { /* Options to enable different TLS functionality in libcoap */ uint8_t verify_peer_cert; /* 1 if peer cert is to be verified */ - uint8_t require_peer_cert; /* 1 if peer cert is required */ - uint8_t allow_self_signed; /* 1 if self signed certs are allowed */ + uint8_t check_common_ca; /* 1 if peer cert is to be signed by + * the same CA as the local cert */ + uint8_t allow_self_signed; /* 1 if self-signed certs are allowed */ + uint8_t allow_self_signed; /* 1 if self-signed certs are allowed. + * Ignored if check_common_ca set */ uint8_t allow_expired_certs; /* 1 if expired certs are allowed */ uint8_t cert_chain_validation; /* 1 if to check cert_chain_verify_depth */ uint8_t cert_chain_verify_depth; /* recommended depth is 3 */ @@ -486,13 +489,20 @@ for different versions of the coap_dtls_pki_t structure in the future. *SECTION: PKI/RPK: coap_dtls_pki_t: Peer Certificate Checking* *verify_peer_cert* Set to 1 to check that the peer's certificate is valid if -provided, else 0. +provided, else 0. If not set, check_common_ca, allow_self_signed, +allow_expired_certs, cert_chain_validation, cert_chain_verify_depth, +check_cert_revocation, allow_no_crl, allow_expired_crl, allow_bad_md_hash +and allow_short_rsa_length settings are all ignored. -*require_peer_cert* Set to 1 to enforce that the peer provides a certificate, -else 0. If the Server, this initiates a request for the peer certificate. +*check_common_ca* Set to 1 to check that the CA that signed the peer's +certificate is the same CA that signed the local certificate +else 0. If set to 1 and *verify_peer_cert* is set to 1, then for the server, a +list of valid CAs are sent to client. For the client, the logic will check +that both the client and server certificates are signed by the same CA. *allow_self_signed* Set to 1 to allow the peer (or any certificate in the -certificate chain) to be a self-signed certificate, else 0. +certificate chain) to be a self-signed certificate, else 0. If +*check_common_ca* is set, then a self-signed certificate will not be allowed. *allow_expired_certs* Set to 1 to allow certificates that have either expired, or are not yet valid to be allowed, else 0. @@ -524,7 +534,11 @@ definition to be valid, else 0. *allow_short_rsa_length* Set to 1 if small RSA keysizes are allowed, else 0. *is_rpk_not_cert* Set to 1 if the Certificate is actually a Raw Public Key. -If set, PKI key format type cannot be COAP_PKI_KEY_PEM. +If set, PKI key format type cannot be COAP_PKI_KEY_PEM. If set, +check_common_ca, allow_self_signed, allow_expired_certs, +cert_chain_validation, cert_chain_verify_depth, check_cert_revocation, +allow_no_crl, allow_expired_crl, allow_bad_md_hash and +allow_short_rsa_length settings are all ignored. *SECTION: PKI/RPK: coap_dtls_pki_t: Reserved* @@ -941,7 +955,7 @@ setup_server_context_pki (const char *public_cert_file, dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION; dtls_pki.verify_peer_cert = 1; - dtls_pki.require_peer_cert = 1; + dtls_pki.check_common_ca = 1; dtls_pki.allow_self_signed = 1; dtls_pki.allow_expired_certs = 1; dtls_pki.cert_chain_validation = 1; diff --git a/man/coap_session.txt.in b/man/coap_session.txt.in index 68a9870c39..9f2d68104d 100644 --- a/man/coap_session.txt.in +++ b/man/coap_session.txt.in @@ -290,7 +290,7 @@ setup_client_session_pki (struct in_addr ip_address, /* See coap_encryption(3) */ dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION; dtls_pki.verify_peer_cert = 1; - dtls_pki.require_peer_cert = 1; + dtls_pki.check_common_ca = 1; dtls_pki.allow_self_signed = 1; dtls_pki.allow_expired_certs = 1; dtls_pki.cert_chain_validation = 1; diff --git a/src/coap_gnutls.c b/src/coap_gnutls.c index fd3bbdd934..beb549bd42 100644 --- a/src/coap_gnutls.c +++ b/src/coap_gnutls.c @@ -97,6 +97,7 @@ typedef struct coap_gnutls_env_t { int established; int doing_dtls_timeout; coap_tick_t last_timeout; + int sent_alert; } coap_gnutls_env_t; #define IS_PSK (1 << 0) @@ -241,8 +242,8 @@ coap_gnutls_log_func(int level, const char* text) */ int coap_dtls_context_set_pki(coap_context_t *c_context, - coap_dtls_pki_t* setup_data, - coap_dtls_role_t role UNUSED) + const coap_dtls_pki_t* setup_data, + const coap_dtls_role_t role UNUSED) { coap_gnutls_context_t *g_context = ((coap_gnutls_context_t *)c_context->dtls_context); @@ -251,6 +252,34 @@ coap_dtls_context_set_pki(coap_context_t *c_context, return 0; g_context->setup_data = *setup_data; + if (!g_context->setup_data.verify_peer_cert) { + /* Needs to be clear so that no CA DNs are transmitted */ + g_context->setup_data.check_common_ca = 0; + if (g_context->setup_data.is_rpk_not_cert) { + /* Disable all of these as they cannot be checked */ + g_context->setup_data.allow_self_signed = 0; + g_context->setup_data.allow_expired_certs = 0; + g_context->setup_data.cert_chain_validation = 0; + g_context->setup_data.cert_chain_verify_depth = 0; + g_context->setup_data.check_cert_revocation = 0; + g_context->setup_data.allow_no_crl = 0; + g_context->setup_data.allow_expired_crl = 0; + g_context->setup_data.allow_bad_md_hash = 0; + g_context->setup_data.allow_short_rsa_length = 0; + } + else { + /* Allow all of these but warn if issue */ + g_context->setup_data.allow_self_signed = 1; + g_context->setup_data.allow_expired_certs = 1; + g_context->setup_data.cert_chain_validation = 1; + g_context->setup_data.cert_chain_verify_depth = 10; + g_context->setup_data.check_cert_revocation = 1; + g_context->setup_data.allow_no_crl = 1; + g_context->setup_data.allow_expired_crl = 1; + g_context->setup_data.allow_bad_md_hash = 1; + g_context->setup_data.allow_short_rsa_length = 1; + } + } g_context->psk_pki_enabled |= IS_PKI; return 1; } @@ -570,6 +599,7 @@ typedef struct { char *san_or_cn; const gnutls_datum_t *cert_list; unsigned int cert_list_size; + int self_signed; /* 1 if cert self-signed, 0 otherwise */ } coap_gnutls_certificate_info_t; /* @@ -586,7 +616,12 @@ static gnutls_certificate_type_t get_san_or_cn(gnutls_session_t g_session, char *cn; int ret; +#if (GNUTLS_VERSION_NUMBER >= 0x030606) + cert_info->certificate_type = gnutls_certificate_type_get2(g_session, + GNUTLS_CTYPE_PEERS); +#else /* < 3.6.6 */ cert_info->certificate_type = gnutls_certificate_type_get(g_session); +#endif /* < 3.6.6 */ if (cert_info->certificate_type != GNUTLS_CRT_X509) return cert_info->certificate_type; @@ -604,6 +639,8 @@ static gnutls_certificate_type_t get_san_or_cn(gnutls_session_t g_session, G_CHECK(gnutls_x509_crt_import(cert, &cert_info->cert_list[0], GNUTLS_X509_FMT_DER), "gnutls_x509_crt_import"); + cert_info->self_signed = gnutls_x509_crt_check_issuer(cert, cert); + size = sizeof(dn) -1; /* See if there is a Subject Alt Name first */ ret = gnutls_x509_crt_get_subject_alt_name(cert, 0, dn, &size, NULL); @@ -663,56 +700,118 @@ static gnutls_certificate_type_t get_san_or_cn(gnutls_session_t g_session, static int cert_verify_gnutls(gnutls_session_t g_session) { unsigned int status = 0; + unsigned int fail = 0; coap_session_t *c_session = (coap_session_t *)gnutls_transport_get_ptr(g_session); coap_gnutls_context_t *g_context = (coap_gnutls_context_t *)c_session->context->dtls_context; + coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls; int alert = GNUTLS_A_BAD_CERTIFICATE; int ret; coap_gnutls_certificate_info_t cert_info; gnutls_certificate_type_t cert_type; memset(&cert_info, 0, sizeof(cert_info)); + cert_type = get_san_or_cn(g_session, &cert_info); +#if (GNUTLS_VERSION_NUMBER >= 0x030606) + if (cert_type == GNUTLS_CRT_RAW) + goto finish; +#endif /* >= 3.6.6 */ G_CHECK(gnutls_certificate_verify_peers(g_session, NULL, 0, &status), "gnutls_certificate_verify_peers"); - cert_type = get_san_or_cn(g_session, &cert_info); - if (status) { status &= ~(GNUTLS_CERT_INVALID); if (status & (GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED)) { + status &= ~(GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED); if (g_context->setup_data.allow_expired_certs) { - status &= ~(GNUTLS_CERT_NOT_ACTIVATED|GNUTLS_CERT_EXPIRED); - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s'\n", coap_session_str(c_session), "The certificate has an invalid usage date", OUTPUT_CERT_NAME); } + else { + fail = 1; + coap_log(LOG_WARNING, + " %s: %s: '%s'\n", + coap_session_str(c_session), + "The certificate has an invalid usage date", + OUTPUT_CERT_NAME); + } } if (status & (GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED| GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)) { + status &= ~(GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED| + GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE); if (g_context->setup_data.allow_expired_crl) { - status &= ~(GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED| - GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE); - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s'\n", coap_session_str(c_session), "The certificate's CRL entry has an invalid usage date", OUTPUT_CERT_NAME); } + else { + fail = 1; + coap_log(LOG_WARNING, + " %s: %s: '%s'\n", + coap_session_str(c_session), + "The certificate's CRL entry has an invalid usage date", + OUTPUT_CERT_NAME); + } + } + if (status & (GNUTLS_CERT_SIGNER_NOT_FOUND)) { + status &= ~(GNUTLS_CERT_SIGNER_NOT_FOUND); + if (cert_info.self_signed) { + if (g_context->setup_data.allow_self_signed && + !g_context->setup_data.check_common_ca) { + coap_log(LOG_INFO, + " %s: %s: overridden: '%s'\n", + coap_session_str(c_session), + "Self-signed", + OUTPUT_CERT_NAME); + } + else { + fail = 1; + alert = GNUTLS_A_UNKNOWN_CA; + coap_log(LOG_WARNING, + " %s: %s: '%s'\n", + coap_session_str(c_session), + "Self-signed", + OUTPUT_CERT_NAME); + } + } + else { + if (!g_context->setup_data.verify_peer_cert) { + coap_log(LOG_INFO, + " %s: %s: overridden: '%s'\n", + coap_session_str(c_session), + "The peer certificate's CA is unknown", + OUTPUT_CERT_NAME); + } + else { + fail = 1; + alert = GNUTLS_A_UNKNOWN_CA; + coap_log(LOG_WARNING, + " %s: %s: '%s'\n", + coap_session_str(c_session), + "The peer certificate's CA is unknown", + OUTPUT_CERT_NAME); + } + } } if (status) { + fail = 1; coap_log(LOG_WARNING, - " %s: status 0x%x: '%s'\n", + " %s: gnutls_certificate_verify_peers() status 0x%x: '%s'\n", coap_session_str(c_session), status, OUTPUT_CERT_NAME); } } - if (status) + if (fail) goto fail; if (g_context->setup_data.validate_cn_call_back) { @@ -753,6 +852,7 @@ static int cert_verify_gnutls(gnutls_session_t g_session) } } +finish: if (cert_info.san_or_cn) gnutls_free(cert_info.san_or_cn); @@ -762,7 +862,11 @@ static int cert_verify_gnutls(gnutls_session_t g_session) if (cert_info.san_or_cn) gnutls_free(cert_info.san_or_cn); - G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, alert)); + if (!g_env->sent_alert) { + G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, alert)); + g_env->sent_alert = 1; + } + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; return 0; } @@ -775,13 +879,8 @@ static int cert_verify_gnutls(gnutls_session_t g_session) */ static int cert_verify_callback_gnutls(gnutls_session_t g_session) { - int ret; - if (gnutls_auth_get_type(g_session) == GNUTLS_CRD_CERTIFICATE) { if (cert_verify_gnutls(g_session) == 0) { - G_ACTION(gnutls_alert_send(g_session, - GNUTLS_AL_FATAL, - GNUTLS_A_ACCESS_DENIED)); return -1; } } @@ -870,6 +969,7 @@ get_asn1_spki(const uint8_t *data, size_t size) */ static int setup_pki_credentials(gnutls_certificate_credentials_t *pki_credentials, + gnutls_session_t g_session, coap_gnutls_context_t *g_context, coap_dtls_pki_t *setup_data, coap_dtls_role_t role) { @@ -1239,6 +1339,8 @@ setup_pki_credentials(gnutls_certificate_credentials_t *pki_credentials, "gnutls_certificate_set_x509_trust_dir"); #endif } + gnutls_certificate_send_x509_rdn_sequence(g_session, + setup_data->check_common_ca ? 0 : 1); if (!(g_context->psk_pki_enabled & IS_PKI)) { /* No PKI defined at all - still need a trust set up for 3.6.0 or later */ G_CHECK(gnutls_certificate_set_x509_system_trust(*pki_credentials), @@ -1246,10 +1348,8 @@ setup_pki_credentials(gnutls_certificate_credentials_t *pki_credentials, } /* Verify Peer */ - if (setup_data->verify_peer_cert) { - gnutls_certificate_set_verify_function(*pki_credentials, - cert_verify_callback_gnutls); - } + gnutls_certificate_set_verify_function(*pki_credentials, + cert_verify_callback_gnutls); /* Cert chain checking (can raise GNUTLS_E_CONSTRAINT_ERROR) */ if (setup_data->cert_chain_validation) { @@ -1263,8 +1363,6 @@ setup_pki_credentials(gnutls_certificate_credentials_t *pki_credentials, * CRL checking (can raise GNUTLS_CERT_MISSING_OCSP_STATUS) */ gnutls_certificate_set_verify_flags(*pki_credentials, - (setup_data->allow_self_signed == 0 ? - GNUTLS_VERIFY_DO_NOT_ALLOW_SAME : 0) | (setup_data->check_cert_revocation == 0 ? GNUTLS_VERIFY_DISABLE_CRL_CHECKS : 0) ); @@ -1378,6 +1476,7 @@ post_client_hello_gnutls_psk(gnutls_session_t g_session) if (!new_entry) { G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, GNUTLS_A_UNRECOGNIZED_NAME)); + g_env->sent_alert = 1; ret = GNUTLS_E_NO_CERTIFICATE_FOUND; goto end; } @@ -1396,6 +1495,7 @@ post_client_hello_gnutls_psk(gnutls_session_t g_session) int keep_ret = ret; G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE)); + g_env->sent_alert = 1; ret = keep_ret; goto end; } @@ -1490,6 +1590,7 @@ post_client_hello_gnutls_pki(gnutls_session_t g_session) if (!new_entry) { G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, GNUTLS_A_UNRECOGNIZED_NAME)); + g_env->sent_alert = 1; ret = GNUTLS_E_NO_CERTIFICATE_FOUND; goto end; } @@ -1503,11 +1604,13 @@ post_client_hello_gnutls_pki(gnutls_session_t g_session) sni_setup_data.pki_key = *new_entry; if ((ret = setup_pki_credentials( &g_context->pki_sni_entry_list[i].pki_credentials, + g_session, g_context, &sni_setup_data, COAP_DTLS_ROLE_SERVER)) < 0) { int keep_ret = ret; G_ACTION(gnutls_alert_send(g_session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE)); + g_env->sent_alert = 1; ret = keep_ret; goto end; } @@ -1590,8 +1693,9 @@ setup_client_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env) * This works providing COAP_PKI_KEY_PEM has a value of 0. */ coap_dtls_pki_t *setup_data = &g_context->setup_data; - G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_context, - setup_data, COAP_DTLS_ROLE_CLIENT), + G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_env->g_session, + g_context, setup_data, + COAP_DTLS_ROLE_CLIENT), "setup_pki_credentials"); G_CHECK(gnutls_credentials_set(g_env->g_session, GNUTLS_CRD_CERTIFICATE, @@ -1718,11 +1822,12 @@ setup_server_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env) if (g_context->psk_pki_enabled & IS_PKI) { coap_dtls_pki_t *setup_data = &g_context->setup_data; - G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_context, - setup_data, COAP_DTLS_ROLE_SERVER), + G_CHECK(setup_pki_credentials(&g_env->pki_credentials, g_env->g_session, + g_context, setup_data, + COAP_DTLS_ROLE_SERVER), "setup_pki_credentials"); - if (setup_data->require_peer_cert) { + if (setup_data->verify_peer_cert) { gnutls_certificate_server_set_request(g_env->g_session, GNUTLS_CERT_REQUIRE); } @@ -1913,7 +2018,7 @@ coap_dtls_free_gnutls_env(coap_gnutls_context_t *g_context, /* It is suggested not to use GNUTLS_SHUT_RDWR in DTLS * connections because the peer's closure message might * be lost */ - if (free_bye != COAP_FREE_BYE_NONE) { + if (free_bye != COAP_FREE_BYE_NONE && !g_env->sent_alert) { /* Only do this if appropriate */ gnutls_bye(g_env->g_session, free_bye == COAP_FREE_BYE_AS_UDP ? GNUTLS_SHUT_WR : GNUTLS_SHUT_RDWR); @@ -1953,14 +2058,17 @@ void *coap_dtls_new_server_session(coap_session_t *c_session) { return g_env; } -static void log_last_alert(gnutls_session_t g_session) { +static void log_last_alert(coap_session_t *c_session, + gnutls_session_t g_session) { int last_alert = gnutls_alert_get(g_session); if (last_alert == GNUTLS_A_CLOSE_NOTIFY) - coap_log(LOG_DEBUG, "Received alert '%d': '%s'\n", + coap_log(LOG_DEBUG, "***%s: Alert '%d': %s\n", + coap_session_str(c_session), last_alert, gnutls_alert_get_name(last_alert)); else - coap_log(LOG_WARNING, "Received alert '%d': '%s'\n", + coap_log(LOG_WARNING, "***%s: Alert '%d': %s\n", + coap_session_str(c_session), last_alert, gnutls_alert_get_name(last_alert)); } @@ -1994,50 +2102,69 @@ do_gnutls_handshake(coap_session_t *c_session, coap_gnutls_env_t *g_env) { "Insufficient credentials provided.\n"); ret = -1; break; - case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: case GNUTLS_E_FATAL_ALERT_RECEIVED: - log_last_alert(g_env->g_session); + /* Stop the sending of an alert on closedown */ + g_env->sent_alert = 1; + log_last_alert(c_session, g_env->g_session); + case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET: c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; case GNUTLS_E_WARNING_ALERT_RECEIVED: - log_last_alert(g_env->g_session); + log_last_alert(c_session, g_env->g_session); c_session->dtls_event = COAP_EVENT_DTLS_ERROR; ret = 0; break; case GNUTLS_E_NO_CERTIFICATE_FOUND: +#if (GNUTLS_VERSION_NUMBER > 0x030606) + case GNUTLS_E_CERTIFICATE_REQUIRED: +#endif /* GNUTLS_VERSION_NUMBER > 0x030606 */ coap_log(LOG_WARNING, "Client Certificate requested and required, but not provided\n" ); G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE)); + g_env->sent_alert = 1; c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; case GNUTLS_E_DECRYPTION_FAILED: coap_log(LOG_WARNING, "do_gnutls_handshake: session establish " - "returned %d: '%s'\n", - ret, gnutls_strerror(ret)); + "returned '%s'\n", + gnutls_strerror(ret)); G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL, GNUTLS_A_DECRYPT_ERROR)); + g_env->sent_alert = 1; c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; + case GNUTLS_E_CERTIFICATE_ERROR: + if (g_env->sent_alert) { + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; + ret = -1; + break; + } + /* Fall through */ case GNUTLS_E_UNKNOWN_CIPHER_SUITE: case GNUTLS_E_NO_CIPHER_SUITES: + case GNUTLS_E_INVALID_SESSION: coap_log(LOG_WARNING, "do_gnutls_handshake: session establish " - "returned %d: '%s'\n", - ret, gnutls_strerror(ret)); - G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL, - GNUTLS_A_HANDSHAKE_FAILURE)); + "returned '%s'\n", + gnutls_strerror(ret)); + if (!g_env->sent_alert) { + G_ACTION(gnutls_alert_send(g_env->g_session, GNUTLS_AL_FATAL, + GNUTLS_A_HANDSHAKE_FAILURE)); + g_env->sent_alert = 1; + } c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; case GNUTLS_E_SESSION_EOF: - /* fall through */ case GNUTLS_E_TIMEDOUT: + case GNUTLS_E_PULL_ERROR: + case GNUTLS_E_PUSH_ERROR: c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; @@ -2070,7 +2197,7 @@ void *coap_dtls_new_client_session(coap_session_t *c_session) { } void coap_dtls_free_session(coap_session_t *c_session) { - if (c_session && c_session->context) { + if (c_session && c_session->context && c_session->tls) { coap_dtls_free_gnutls_env(c_session->context->dtls_context, c_session->tls, COAP_PROTO_NOT_RELIABLE(c_session->proto) ? @@ -2113,11 +2240,17 @@ int coap_dtls_send(coap_session_t *c_session, ret = 0; break; case GNUTLS_E_FATAL_ALERT_RECEIVED: - log_last_alert(g_env->g_session); - c_session->dtls_event = COAP_EVENT_DTLS_ERROR; + /* Stop the sending of an alert on closedown */ + g_env->sent_alert = 1; + log_last_alert(c_session, g_env->g_session); + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; default: + coap_log(LOG_DEBUG, + "coap_dtls_send: gnutls_record_send " + "returned %d: '%s'\n", + ret, gnutls_strerror(ret)); ret = -1; break; } @@ -2238,12 +2371,14 @@ coap_dtls_receive(coap_session_t *c_session, else { switch (ret) { case GNUTLS_E_FATAL_ALERT_RECEIVED: - log_last_alert(g_env->g_session); + /* Stop the sending of an alert on closedown */ + g_env->sent_alert = 1; + log_last_alert(c_session, g_env->g_session); c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; case GNUTLS_E_WARNING_ALERT_RECEIVED: - log_last_alert(g_env->g_session); + log_last_alert(c_session, g_env->g_session); c_session->dtls_event = COAP_EVENT_DTLS_ERROR; ret = 0; break; @@ -2262,7 +2397,7 @@ coap_dtls_receive(coap_session_t *c_session, } else { ret = -1; - if (ssl_data->pdu_len) { + if (ssl_data->pdu_len && !g_env->sent_alert) { /* Do the handshake again incase of internal timeout */ ret = do_gnutls_handshake(c_session, g_env); if (ret == 1) { @@ -2274,7 +2409,9 @@ coap_dtls_receive(coap_session_t *c_session, } if (c_session->dtls_event >= 0) { - coap_handle_event(c_session->context, c_session->dtls_event, c_session); + /* COAP_EVENT_DTLS_CLOSED event reported in coap_session_disconnected() */ + if (c_session->dtls_event != COAP_EVENT_DTLS_CLOSED) + coap_handle_event(c_session->context, c_session->dtls_event, c_session); if (c_session->dtls_event == COAP_EVENT_DTLS_ERROR || c_session->dtls_event == COAP_EVENT_DTLS_CLOSED) { coap_session_disconnected(c_session, COAP_NACK_TLS_FAILED); @@ -2395,10 +2532,10 @@ coap_sock_read(gnutls_transport_ptr_t context, void *out, size_t outl) { ret = recv(c_session->sock.fd, out, outl, 0); #endif if (ret > 0) { - coap_log(LOG_DEBUG, "* %s: setup: received %d bytes\n", + coap_log(LOG_DEBUG, "* %s: received %d bytes\n", coap_session_str(c_session), ret); } else if (ret < 0 && errno != EAGAIN) { - coap_log(LOG_DEBUG, "* %s: setup: failed to receive any bytes (%s)\n", + coap_log(LOG_DEBUG, "* %s: failed to receive any bytes (%s)\n", coap_session_str(c_session), coap_socket_strerror()); } if (ret == 0) { @@ -2426,11 +2563,30 @@ coap_sock_write(gnutls_transport_ptr_t context, const void *in, size_t inl) { ret = (int)coap_socket_write(&c_session->sock, in, inl); if (ret > 0) { - coap_log(LOG_DEBUG, "* %s: setup: sent %d bytes\n", + coap_log(LOG_DEBUG, "* %s: sent %d bytes\n", coap_session_str(c_session), ret); } else if (ret < 0) { - coap_log(LOG_DEBUG, "* %s: setup: failed to send %d bytes (%s)\n", - coap_session_str(c_session), ret, coap_socket_strerror()); + if ((c_session->state == COAP_SESSION_STATE_CSM || + c_session->state == COAP_SESSION_STATE_HANDSHAKE) && + (errno == EPIPE || errno == ECONNRESET)) { + /* + * Need to handle a TCP timing window where an agent continues with + * the sending of the next handshake or a CSM. + * However, the peer does not like a certificate and so sends a + * fatal alert and closes the TCP session. + * The sending of the next handshake or CSM may get terminated because + * of the closed TCP session, but there is still an outstanding alert + * to be read in and reported on. + * In this case, pretend that sending the info was fine so that the + * alert can be read (which effectively is what happens with DTLS). + */ + ret = inl; + } + else { + coap_log(LOG_DEBUG, "* %s: failed to send %zd bytes (%s) state %d\n", + coap_session_str(c_session), inl, coap_socket_strerror(), + c_session->state); + } } if (ret == 0) { errno = EAGAIN; @@ -2553,9 +2709,17 @@ ssize_t coap_tls_write(coap_session_t *c_session, case GNUTLS_E_AGAIN: ret = 0; break; + case GNUTLS_E_PUSH_ERROR: + case GNUTLS_E_PULL_ERROR: + case GNUTLS_E_PREMATURE_TERMINATION: + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; + ret = -1; + break; case GNUTLS_E_FATAL_ALERT_RECEIVED: - log_last_alert(g_env->g_session); - c_session->dtls_event = COAP_EVENT_DTLS_ERROR; + /* Stop the sending of an alert on closedown */ + g_env->sent_alert = 1; + log_last_alert(c_session, g_env->g_session); + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; break; default: @@ -2567,7 +2731,7 @@ ssize_t coap_tls_write(coap_session_t *c_session, break; } if (ret == -1) { - coap_log(LOG_WARNING, "coap_dtls_send: cannot send PDU\n"); + coap_log(LOG_INFO, "coap_tls_write: cannot send PDU\n"); } } } @@ -2607,13 +2771,13 @@ ssize_t coap_tls_read(coap_session_t *c_session, size_t data_len ) { coap_gnutls_env_t *g_env = (coap_gnutls_env_t *)c_session->tls; - int ret; + int ret = -1; if (!g_env) return -1; c_session->dtls_event = -1; - if (!g_env->established) { + if (!g_env->established && !g_env->sent_alert) { ret = do_gnutls_handshake(c_session, g_env); if (ret == 1) { coap_handle_event(c_session->context, COAP_EVENT_DTLS_CONNECTED, @@ -2621,7 +2785,7 @@ ssize_t coap_tls_read(coap_session_t *c_session, coap_session_send_csm(c_session); } } - if (g_env->established) { + if (c_session->state != COAP_SESSION_STATE_NONE && g_env->established) { ret = gnutls_record_recv(g_env->g_session, data, (int)data_len); if (ret <= 0) { switch (ret) { @@ -2635,6 +2799,18 @@ ssize_t coap_tls_read(coap_session_t *c_session, case GNUTLS_E_PULL_ERROR: c_session->dtls_event = COAP_EVENT_DTLS_ERROR; break; + case GNUTLS_E_FATAL_ALERT_RECEIVED: + /* Stop the sending of an alert on closedown */ + g_env->sent_alert = 1; + log_last_alert(c_session, g_env->g_session); + c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; + ret = -1; + break; + case GNUTLS_E_WARNING_ALERT_RECEIVED: + log_last_alert(c_session, g_env->g_session); + c_session->dtls_event = COAP_EVENT_DTLS_ERROR; + ret = 0; + break; default: coap_log(LOG_WARNING, "coap_tls_read: gnutls_record_recv " diff --git a/src/coap_io.c b/src/coap_io.c index 07fa254f4f..5ef9fe3cd8 100644 --- a/src/coap_io.c +++ b/src/coap_io.c @@ -430,8 +430,14 @@ coap_socket_write(coap_socket_t *sock, const uint8_t *data, size_t data_len) { #endif /* COAP_EPOLL_SUPPORT */ return 0; } - coap_log(LOG_WARNING, "coap_socket_write: send: %s\n", - coap_socket_strerror()); + if (errno == EPIPE || errno == ECONNRESET) { + coap_log(LOG_INFO, "coap_socket_write: send: %s\n", + coap_socket_strerror()); + } + else { + coap_log(LOG_WARNING, "coap_socket_write: send: %s\n", + coap_socket_strerror()); + } return -1; } if (r < (ssize_t)data_len) { diff --git a/src/coap_mbedtls.c b/src/coap_mbedtls.c index bfe3350c4c..627e57666d 100644 --- a/src/coap_mbedtls.c +++ b/src/coap_mbedtls.c @@ -1,7 +1,7 @@ /* * coap_mbedtls.c -- mbedTLS Datagram Transport Layer Support for libcoap * -* Copyright (C) 2019-2020 Jon Shallow +* Copyright (C) 2019-2021 Jon Shallow * 2019 Jitin George * * This file is part of the CoAP library libcoap. Please see README for terms @@ -32,6 +32,7 @@ * MBEDTLS_SSL_CLI_C - defined for client side functionality * MBEDTLS_SSL_PROTO_DTLS - defined for DTLS support * MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED - defined if PSK is to be supported + * or MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED - defined if PSK is to be supported * * Note: TLS is not currently supported until additional code is added */ @@ -59,6 +60,13 @@ #define mbedtls_strdup(a) strdup(a) #define mbedtls_strndup(a,b) strndup(a,b) +#ifndef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED +/* definition changed in later mbedtls code versions */ +#ifdef MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED +#define MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED +#endif /* MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED */ +#endif /* ! MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */ + #ifdef __GNUC__ #define UNUSED __attribute__((unused)) #else /* __GNUC__ */ @@ -305,18 +313,12 @@ cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, if (*flags == 0) return 0; - if (!setup_data->verify_peer_cert) { - /* Nothing is being checked */ - *flags = 0; - return 0; - } - cn = get_san_or_cn_from_cert(crt); if (*flags & MBEDTLS_X509_BADCERT_EXPIRED) { if (setup_data->allow_expired_certs) { *flags &= ~MBEDTLS_X509_BADCERT_EXPIRED; - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth %d\n", coap_session_str(c_session), "The certificate has expired", cn ? cn : "?", depth); @@ -325,7 +327,7 @@ cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, if (*flags & MBEDTLS_X509_BADCERT_FUTURE) { if (setup_data->allow_expired_certs) { *flags &= ~MBEDTLS_X509_BADCERT_FUTURE; - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth %d\n", coap_session_str(c_session), "The certificate has a future date", cn ? cn : "?", depth); @@ -334,7 +336,7 @@ cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, if (*flags & MBEDTLS_X509_BADCERT_BAD_MD) { if (setup_data->allow_bad_md_hash) { *flags &= ~MBEDTLS_X509_BADCERT_BAD_MD; - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth %d\n", coap_session_str(c_session), "The certificate has a bad MD hash", cn ? cn : "?", depth); @@ -343,16 +345,41 @@ cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, if (*flags & MBEDTLS_X509_BADCERT_BAD_KEY) { if (setup_data->allow_short_rsa_length) { *flags &= ~MBEDTLS_X509_BADCERT_BAD_KEY; - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth %d\n", coap_session_str(c_session), "The certificate has a short RSA length", cn ? cn : "?", depth); } } + if (*flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { + uint32_t lflags; + int self_signed = !mbedtls_x509_crt_verify(crt, crt, NULL, NULL, &lflags, + NULL, NULL); + if (self_signed && depth == 0) { + if (setup_data->allow_self_signed && + !setup_data->check_common_ca) { + *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; + coap_log(LOG_INFO, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "Self-signed", + cn ? cn : "?", depth); + } + } + else { + if (!setup_data->verify_peer_cert) { + *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; + coap_log(LOG_INFO, + " %s: %s: overridden: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate's CA does not match", cn ? cn : "?", depth); + } + } + } if (*flags & MBEDTLS_X509_BADCRL_EXPIRED) { if (setup_data->check_cert_revocation && setup_data->allow_expired_crl) { *flags &= ~MBEDTLS_X509_BADCRL_EXPIRED; - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth %d\n", coap_session_str(c_session), "The certificate's CRL has expired", cn ? cn : "?", depth); @@ -364,7 +391,7 @@ cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, if (*flags & MBEDTLS_X509_BADCRL_FUTURE) { if (setup_data->check_cert_revocation && setup_data->allow_expired_crl) { *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth %d\n", coap_session_str(c_session), "The certificate's CRL has a future date", cn ? cn : "?", depth); @@ -373,6 +400,15 @@ cert_verify_callback_mbedtls(void *data, mbedtls_x509_crt *crt, *flags &= ~MBEDTLS_X509_BADCRL_FUTURE; } } + if (setup_data->cert_chain_validation && + depth > (setup_data->cert_chain_verify_depth)) { + *flags |= MBEDTLS_X509_BADCERT_OTHER; + coap_log(LOG_WARNING, + " %s: %s: '%s' depth %d\n", + coap_session_str(c_session), + "The certificate's verify depth is too long", + cn ? cn : "?", depth); + } if (*flags & MBEDTLS_X509_BADCERT_CN_MISMATCH) { *flags &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; @@ -483,9 +519,6 @@ setup_pki_credentials(mbedtls_x509_crt *cacert, -ret, get_error_string(ret)); return ret; } - mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? - MBEDTLS_SSL_VERIFY_REQUIRED : - MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); } break; @@ -593,9 +626,6 @@ setup_pki_credentials(mbedtls_x509_crt *cacert, -ret, get_error_string(ret)); return ret; } - mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? - MBEDTLS_SSL_VERIFY_REQUIRED : - MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); } break; @@ -649,9 +679,6 @@ setup_pki_credentials(mbedtls_x509_crt *cacert, -ret, get_error_string(ret)); return ret; } - mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->require_peer_cert ? - MBEDTLS_SSL_VERIFY_REQUIRED : - MBEDTLS_SSL_VERIFY_OPTIONAL); mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); } break; @@ -687,6 +714,13 @@ setup_pki_credentials(mbedtls_x509_crt *cacert, mbedtls_ssl_conf_ca_chain(&m_env->conf, cacert, NULL); } + mbedtls_ssl_conf_cert_req_ca_list(&m_env->conf, + setup_data->check_common_ca ? + MBEDTLS_SSL_CERT_REQ_CA_LIST_ENABLED : + MBEDTLS_SSL_CERT_REQ_CA_LIST_DISABLED); + mbedtls_ssl_conf_authmode(&m_env->conf, setup_data->verify_peer_cert ? + MBEDTLS_SSL_VERIFY_REQUIRED : + MBEDTLS_SSL_VERIFY_NONE); /* * Verify Peer. * Need to do all checking, even if setup_data->verify_peer_cert is not set @@ -744,6 +778,8 @@ pki_sni_callback(void *p_info, mbedtls_ssl_context *ssl, m_context->pki_sni_entry_list = mbedtls_realloc(m_context->pki_sni_entry_list, (i+1)*sizeof(pki_sni_entry)); + memset(&m_context->pki_sni_entry_list[i], 0, + sizeof(m_context->pki_sni_entry_list[i])); m_context->pki_sni_entry_list[i].sni = name; m_context->pki_sni_entry_list[i].pki_key = *new_entry; sni_setup_data = m_context->setup_data; @@ -869,6 +905,8 @@ static int setup_server_ssl_session(coap_session_t *c_session, if (c_session->context->spsk_setup_data.validate_sni_call_back) { mbedtls_ssl_conf_sni(&m_env->conf, psk_sni_callback, c_session); } +#else /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */ + coap_log(LOG_WARNING, "PSK not enabled in MbedTLS library\n"); #endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */ } #endif /* MBEDTLS_SSL_PROTO_DTLS */ @@ -1039,7 +1077,9 @@ static int setup_client_ssl_session(coap_session_t *c_session, /* Identity Hint currently not supported in MbedTLS so code removed */ set_ciphersuites(&m_env->conf, COAP_ENC_PSK); -#endif /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */ +#else /* MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */ + coap_log(LOG_WARNING, "PSK not enabled in MbedTLS library\n"); +#endif /* ! MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED */ } else if ((m_context->psk_pki_enabled & IS_PKI) || (m_context->psk_pki_enabled & (IS_PSK | IS_PKI)) == 0) { @@ -1106,6 +1146,20 @@ coap_dtls_free_mbedtls_env(coap_mbedtls_env_t *m_env) { } } +static const char * +report_mbedtls_alert(unsigned char alert) { + switch (alert) { + case MBEDTLS_SSL_ALERT_MSG_BAD_RECORD_MAC: return ": Bad Record MAC"; + case MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE: return ": Handshake failure"; + case MBEDTLS_SSL_ALERT_MSG_NO_CERT: return ": No Certificate provided"; + case MBEDTLS_SSL_ALERT_MSG_BAD_CERT: return ": Certificate is bad"; + case MBEDTLS_SSL_ALERT_MSG_UNKNOWN_CA: return ": CA is unknown"; + case MBEDTLS_SSL_ALERT_MSG_ACCESS_DENIED: return ": Access was denied"; + case MBEDTLS_SSL_ALERT_MSG_DECRYPT_ERROR: return ": Decrypt error"; + default: return ""; + } +} + /* * return -1 failure * 0 not completed @@ -1114,6 +1168,7 @@ coap_dtls_free_mbedtls_env(coap_mbedtls_env_t *m_env) { static int do_mbedtls_handshake(coap_session_t *c_session, coap_mbedtls_env_t *m_env) { int ret; + int alert; ret = mbedtls_ssl_handshake(&m_env->ssl); switch (ret) { @@ -1129,16 +1184,24 @@ static int do_mbedtls_handshake(coap_session_t *c_session, ret = 0; break; case MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED: - coap_log(LOG_INFO, "hello verification requested\n"); - ret = -1; - mbedtls_ssl_session_reset(&m_env->ssl); - break; + coap_log(LOG_DEBUG, "hello verification requested\n"); + goto reset; case MBEDTLS_ERR_SSL_INVALID_MAC: - coap_log(LOG_INFO, "MAC verification failed\n"); - ret = -1; - mbedtls_ssl_session_reset(&m_env->ssl); - break; + goto fail; + case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: + alert = MBEDTLS_SSL_ALERT_MSG_NO_CERT; + goto fail_alert; + case MBEDTLS_ERR_SSL_BAD_HS_CLIENT_HELLO: + alert = MBEDTLS_SSL_ALERT_MSG_HANDSHAKE_FAILURE; + goto fail_alert; + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: + goto fail; case MBEDTLS_ERR_SSL_FATAL_ALERT_MESSAGE: + if (m_env->ssl.in_msg[1] != MBEDTLS_SSL_ALERT_MSG_CLOSE_NOTIFY) + coap_log(LOG_WARNING, "***%s: Alert '%d'%s\n", + coap_session_str(c_session), m_env->ssl.in_msg[1], + report_mbedtls_alert(m_env->ssl.in_msg[1])); + /* Fall through */ case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: c_session->dtls_event = COAP_EVENT_DTLS_CLOSED; ret = -1; @@ -1152,6 +1215,20 @@ static int do_mbedtls_handshake(coap_session_t *c_session, break; } return ret; + +fail_alert: + mbedtls_ssl_send_alert_message(&m_env->ssl, + MBEDTLS_SSL_ALERT_LEVEL_FATAL, + alert); +fail: + c_session->dtls_event = COAP_EVENT_DTLS_ERROR; + coap_log(LOG_WARNING, + "do_mbedtls_handshake: session establish " + "returned '%s'\n", + get_error_string(ret)); +reset: + mbedtls_ssl_session_reset(&m_env->ssl); + return -1; } static void @@ -1328,14 +1405,28 @@ coap_dtls_context_set_cpsk(coap_context_t *c_context, return 1; } -int coap_dtls_context_set_pki(struct coap_context_t *c_context, - coap_dtls_pki_t *setup_data, - coap_dtls_role_t role UNUSED) +int coap_dtls_context_set_pki(coap_context_t *c_context, + const coap_dtls_pki_t *setup_data, + const coap_dtls_role_t role UNUSED) { coap_mbedtls_context_t *m_context = ((coap_mbedtls_context_t *)c_context->dtls_context); m_context->setup_data = *setup_data; + if (!m_context->setup_data.verify_peer_cert) { + /* Needs to be clear so that no CA DNs are transmitted */ + m_context->setup_data.check_common_ca = 0; + /* Allow all of these but warn if issue */ + m_context->setup_data.allow_self_signed = 1; + m_context->setup_data.allow_expired_certs = 1; + m_context->setup_data.cert_chain_validation = 1; + m_context->setup_data.cert_chain_verify_depth = 10; + m_context->setup_data.check_cert_revocation = 1; + m_context->setup_data.allow_no_crl = 1; + m_context->setup_data.allow_expired_crl = 1; + m_context->setup_data.allow_bad_md_hash = 1; + m_context->setup_data.allow_short_rsa_length = 1; + } m_context->psk_pki_enabled |= IS_PKI; return 1; } @@ -1410,6 +1501,11 @@ void coap_dtls_free_context(void *dtls_context) if (m_context->psk_sni_entry_list) mbedtls_free(m_context->psk_sni_entry_list); + if (m_context->root_ca_path) + mbedtls_free(m_context->root_ca_path); + if (m_context->root_ca_file) + mbedtls_free(m_context->root_ca_file); + mbedtls_free(m_context); } @@ -1456,7 +1552,7 @@ void *coap_dtls_new_server_session(coap_session_t *c_session) void coap_dtls_free_session(coap_session_t *c_session) { - if (c_session && c_session->context) { + if (c_session && c_session->context && c_session->tls) { coap_dtls_free_mbedtls_env(c_session->tls); c_session->tls = NULL; coap_handle_event(c_session->context, COAP_EVENT_DTLS_CLOSED, c_session); diff --git a/src/coap_notls.c b/src/coap_notls.c index b821064110..0d07a716d9 100644 --- a/src/coap_notls.c +++ b/src/coap_notls.c @@ -37,8 +37,8 @@ coap_get_tls_library_version(void) { int coap_dtls_context_set_pki(coap_context_t *ctx UNUSED, - coap_dtls_pki_t* setup_data UNUSED, - coap_dtls_role_t role UNUSED + const coap_dtls_pki_t* setup_data UNUSED, + const coap_dtls_role_t role UNUSED ) { return 0; } diff --git a/src/coap_openssl.c b/src/coap_openssl.c index cfe5bdfcb0..5ca78936c0 100644 --- a/src/coap_openssl.c +++ b/src/coap_openssl.c @@ -558,15 +558,19 @@ static void coap_dtls_info_callback(const SSL *ssl, int where, int ret) { coap_log(LOG_DEBUG, "* %s: %s:%s\n", coap_session_str(session), pstr, SSL_state_string_long(ssl)); } else if (where & SSL_CB_ALERT) { + int log_level = LOG_INFO; pstr = (where & SSL_CB_READ) ? "read" : "write"; - if (dtls_log_level >= LOG_INFO) - coap_log(LOG_INFO, "* %s: SSL3 alert %s:%s:%s\n", + if ((where & (SSL_CB_WRITE|SSL_CB_READ)) && (ret >> 8) == SSL3_AL_FATAL) { + session->dtls_event = COAP_EVENT_DTLS_ERROR; + if ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY) + log_level = LOG_WARNING; + } + if (dtls_log_level >= log_level) + coap_log(log_level, "* %s: SSL3 alert %s:%s:%s\n", coap_session_str(session), pstr, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); - if ((where & (SSL_CB_WRITE|SSL_CB_READ)) && (ret >> 8) == SSL3_AL_FATAL) - session->dtls_event = COAP_EVENT_DTLS_ERROR; } else if (where & SSL_CB_EXIT) { if (ret == 0) { if (dtls_log_level >= LOG_WARNING) { @@ -636,6 +640,29 @@ static int coap_sock_write(BIO *a, const char *in, int inl) { ret = -1; } else { BIO_clear_retry_flags(a); + if (ret == -1) { + if ((session->state == COAP_SESSION_STATE_CSM || + session->state == COAP_SESSION_STATE_HANDSHAKE) && + (errno == EPIPE || errno == ECONNRESET)) { + /* + * Need to handle a TCP timing window where an agent continues with + * the sending of the next handshake or a CSM. + * However, the peer does not like a certificate and so sends a + * fatal alert and closes the TCP session. + * The sending of the next handshake or CSM may get terminated because + * of the closed TCP session, but there is still an outstanding alert + * to be read in and reported on. + * In this case, pretend that sending the info was fine so that the + * alert can be read (which effectively is what happens with DTLS). + */ + ret = inl; + } + else { + coap_log(LOG_DEBUG, "* %s: failed to send %d bytes (%s) state %d\n", + coap_session_str(session), inl, coap_socket_strerror(), + session->state); + } + } } return ret; } @@ -953,7 +980,7 @@ missing_ENGINE_load_cert (const char *cert_id) #if OPENSSL_VERSION_NUMBER < 0x10101000L static int setup_pki_server(SSL_CTX *ctx, - coap_dtls_pki_t* setup_data + const coap_dtls_pki_t* setup_data ) { switch (setup_data->pki_key.key_type) { case COAP_PKI_KEY_PEM: @@ -993,23 +1020,27 @@ setup_pki_server(SSL_CTX *ctx, return 0; } - if (setup_data->pki_key.key.pem.ca_file && + if (setup_data->check_common_ca && setup_data->pki_key.key.pem.ca_file && setup_data->pki_key.key.pem.ca_file[0]) { STACK_OF(X509_NAME) *cert_names; X509_STORE *st; BIO *in; X509 *x = NULL; char *rw_var = NULL; - cert_names = SSL_load_client_CA_file(setup_data->pki_key.key.pem.ca_file); - if (cert_names != NULL) - SSL_CTX_set_client_CA_list(ctx, cert_names); - else { - coap_log(LOG_WARNING, - "*** setup_pki: (D)TLS: %s: Unable to configure " - "client CA File\n", - setup_data->pki_key.key.pem.ca_file); - return 0; + if (role == COAP_DTLS_ROLE_SERVER) { + cert_names = SSL_load_client_CA_file(setup_data->pki_key.key.pem.ca_file); + if (cert_names != NULL) + SSL_CTX_set_client_CA_list(ctx, cert_names); + else { + coap_log(LOG_WARNING, + "*** setup_pki: (D)TLS: %s: Unable to configure " + "client CA File\n", + setup_data->pki_key.key.pem.ca_file); + return 0; + } } + + /* Add CA to the trusted root CA store */ st = SSL_CTX_get_cert_store(ctx); in = BIO_new(BIO_s_file()); /* Need to do this to not get a compiler warning about const parameters */ @@ -1381,7 +1412,7 @@ setup_pki_ssl(SSL *ssl, role == COAP_DTLS_ROLE_SERVER ? "Server" : "Client"); return 0; } - if (setup_data->pki_key.key.pem.ca_file && + if (setup_data->check_common_ca && setup_data->pki_key.key.pem.ca_file && setup_data->pki_key.key.pem.ca_file[0]) { X509_STORE *st; BIO *in; @@ -1824,8 +1855,11 @@ tls_verify_call_back(int preverify_ok, X509_STORE_CTX *ctx) { preverify_ok = 1; break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: - case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: - if (setup_data->allow_self_signed) + if (setup_data->allow_self_signed && !setup_data->check_common_ca) + preverify_ok = 1; + break; + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: /* Set if the CA is not known */ + if (!setup_data->verify_peer_cert) preverify_ok = 1; break; case X509_V_ERR_UNABLE_TO_GET_CRL: @@ -1837,19 +1871,36 @@ tls_verify_call_back(int preverify_ok, X509_STORE_CTX *ctx) { if (setup_data->allow_expired_crl) preverify_ok = 1; break; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + if (!setup_data->verify_peer_cert) + preverify_ok = 1; + break; default: break; } + if (setup_data->cert_chain_validation && + depth > (setup_data->cert_chain_verify_depth)) { + preverify_ok = 0; + err = X509_V_ERR_CERT_CHAIN_TOO_LONG; + X509_STORE_CTX_set_error(ctx, err); + } if (!preverify_ok) { + if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) { + coap_log(LOG_WARNING, + " %s: %s: '%s' depth=%d\n", + coap_session_str(session), + "Unknown CA", cn ? cn : "?", depth); + } + else { coap_log(LOG_WARNING, " %s: %s: '%s' depth=%d\n", coap_session_str(session), X509_verify_cert_error_string(err), cn ? cn : "?", depth); - /* Invoke the CN callback function for this failure */ - keep_preverify_ok = 1; + } } else { - coap_log(LOG_WARNING, + coap_log(LOG_INFO, " %s: %s: overridden: '%s' depth=%d\n", coap_session_str(session), X509_verify_cert_error_string(err), cn ? cn : "?", depth); @@ -1928,27 +1979,19 @@ tls_secret_call_back(SSL *ssl, coap_session_str(session)); if (setup_data->verify_peer_cert) { - if (setup_data->require_peer_cert) { - SSL_set_verify(ssl, - SSL_VERIFY_PEER | - SSL_VERIFY_CLIENT_ONCE | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - tls_verify_call_back); - } - else { - SSL_set_verify(ssl, - SSL_VERIFY_PEER | - SSL_VERIFY_CLIENT_ONCE, - tls_verify_call_back); - } + SSL_set_verify(ssl, + SSL_VERIFY_PEER | + SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + tls_verify_call_back); } else { - SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, tls_verify_call_back); } /* Check CA Chain */ if (setup_data->cert_chain_validation) - SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth); + SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth + 1); /* Certificate Revocation */ if (setup_data->check_cert_revocation) { @@ -2058,7 +2101,7 @@ tls_server_name_call_back(SSL *ssl, SSL_CTX_set_alpn_select_cb(ctx, server_alpn_callback, NULL); } #endif /* !COAP_DISABLE_TCP */ - memset(&sni_setup_data, 0, sizeof(sni_setup_data)); + sni_setup_data = *setup_data; sni_setup_data.pki_key = *new_entry; setup_pki_server(ctx, &sni_setup_data); @@ -2355,7 +2398,7 @@ tls_client_hello_call_back(SSL *ssl, if (sni_tmp) { OPENSSL_free(sni_tmp); } - memset(&sni_setup_data, 0, sizeof(sni_setup_data)); + sni_setup_data = *setup_data; sni_setup_data.pki_key = context->sni_entry_list[i].pki_key; setup_pki_ssl(ssl, &sni_setup_data, 1); } @@ -2367,27 +2410,19 @@ tls_client_hello_call_back(SSL *ssl, coap_session_str(session)); if (setup_data->verify_peer_cert) { - if (setup_data->require_peer_cert) { - SSL_set_verify(ssl, - SSL_VERIFY_PEER | - SSL_VERIFY_CLIENT_ONCE | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - tls_verify_call_back); - } - else { - SSL_set_verify(ssl, - SSL_VERIFY_PEER | - SSL_VERIFY_CLIENT_ONCE, - tls_verify_call_back); - } + SSL_set_verify(ssl, + SSL_VERIFY_PEER | + SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + tls_verify_call_back); } else { - SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, tls_verify_call_back); } /* Check CA Chain */ if (setup_data->cert_chain_validation) - SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth); + SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth + 1); /* Certificate Revocation */ if (setup_data->check_cert_revocation) { @@ -2525,8 +2560,8 @@ psk_tls_client_hello_call_back(SSL *ssl, int coap_dtls_context_set_pki(coap_context_t *ctx, - coap_dtls_pki_t *setup_data, - coap_dtls_role_t role + const coap_dtls_pki_t *setup_data, + const coap_dtls_role_t role ) { coap_openssl_context_t *context = ((coap_openssl_context_t *)ctx->dtls_context); @@ -2534,6 +2569,20 @@ coap_dtls_context_set_pki(coap_context_t *ctx, if (!setup_data) return 0; context->setup_data = *setup_data; + if (!context->setup_data.verify_peer_cert) { + /* Needs to be clear so that no CA DNs are transmitted */ + context->setup_data.check_common_ca = 0; + /* Allow all of these but warn if issue */ + context->setup_data.allow_self_signed = 1; + context->setup_data.allow_expired_certs = 1; + context->setup_data.cert_chain_validation = 1; + context->setup_data.cert_chain_verify_depth = 10; + context->setup_data.check_cert_revocation = 1; + context->setup_data.allow_no_crl = 1; + context->setup_data.allow_expired_crl = 1; + context->setup_data.allow_bad_md_hash = 1; + context->setup_data.allow_short_rsa_length = 1; + } if (role == COAP_DTLS_ROLE_SERVER) { if (context->dtls.ctx) { /* SERVER DTLS */ @@ -2806,13 +2855,17 @@ setup_client_ssl_session(coap_session_t *session, SSL *ssl /* Verify Peer */ if (setup_data->verify_peer_cert) - SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_call_back); + SSL_set_verify(ssl, + SSL_VERIFY_PEER | + SSL_VERIFY_CLIENT_ONCE | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + tls_verify_call_back); else - SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + SSL_set_verify(ssl, SSL_VERIFY_NONE, tls_verify_call_back); /* Check CA Chain */ if (setup_data->cert_chain_validation) - SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth); + SSL_set_verify_depth(ssl, setup_data->cert_chain_verify_depth + 1); } return 1; @@ -3269,7 +3322,7 @@ ssize_t coap_tls_write(coap_session_t *session, } r = 0; } else { - coap_log(LOG_WARNING, "***%s: coap_tls_write: cannot send PDU\n", + coap_log(LOG_INFO, "***%s: coap_tls_write: cannot send PDU\n", coap_session_str(session)); if (err == SSL_ERROR_ZERO_RETURN) session->dtls_event = COAP_EVENT_DTLS_CLOSED; diff --git a/src/coap_tinydtls.c b/src/coap_tinydtls.c index c1d0210b2b..fce43de23b 100644 --- a/src/coap_tinydtls.c +++ b/src/coap_tinydtls.c @@ -966,8 +966,8 @@ pem_decode_mem_asn1(const char *begstr, const uint8_t *str) int coap_dtls_context_set_pki(coap_context_t *ctx, - coap_dtls_pki_t* setup_data, - coap_dtls_role_t role UNUSED + const coap_dtls_pki_t* setup_data, + const coap_dtls_role_t role UNUSED ) { #ifdef DTLS_ECC coap_tiny_context_t *t_context; @@ -1130,6 +1130,7 @@ coap_dtls_context_set_pki_root_cas(struct coap_context_t *ctx UNUSED, const char *ca_file UNUSED, const char *ca_path UNUSED ) { + coap_log(LOG_WARNING, "Root CAs PKI not supported\n"); return 0; } diff --git a/src/net.c b/src/net.c index eac9257322..2abc92b8e0 100644 --- a/src/net.c +++ b/src/net.c @@ -412,7 +412,7 @@ int coap_context_set_psk2(coap_context_t *ctx, } int coap_context_set_pki(coap_context_t *ctx, - coap_dtls_pki_t* setup_data + const coap_dtls_pki_t* setup_data ) { if (!setup_data) return 0; @@ -1508,7 +1508,7 @@ coap_read_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now) session->read_header[0] = *p++; bytes_read -= 1; if (!coap_pdu_parse_header_size(session->proto, - session->read_header)) { + session->read_header)) { bytes_read = -1; break; }