diff --git a/examples/coap-client.c b/examples/coap-client.c index c279595d..bc6c800a 100644 --- a/examples/coap-client.c +++ b/examples/coap-client.c @@ -1251,18 +1251,18 @@ setup_pki(coap_context_t *ctx) { dtls_pki.check_cert_revocation = 1; dtls_pki.allow_no_crl = 1; dtls_pki.allow_expired_crl = 1; - dtls_pki.validate_cn_call_back = verify_cn_callback; - dtls_pki.cn_call_back_arg = NULL; - dtls_pki.validate_sni_call_back = NULL; - dtls_pki.sni_call_back_arg = NULL; } + else if (is_rpk_not_cert) { + dtls_pki.verify_peer_cert = verify_peer_cert; + } + dtls_pki.is_rpk_not_cert = is_rpk_not_cert; + dtls_pki.validate_cn_call_back = verify_cn_callback; if ((uri.host.length == 3 && memcmp(uri.host.s, "::1", 3) != 0) || (uri.host.length == 9 && memcmp(uri.host.s, "127.0.0.1", 9) != 0)) memcpy(client_sni, uri.host.s, min(uri.host.length, sizeof(client_sni)-1)); else memcpy(client_sni, "localhost", 9); - dtls_pki.is_rpk_not_cert = is_rpk_not_cert; dtls_pki.client_sni = client_sni; if ((key_file && strncasecmp (key_file, "pkcs11:", 7) == 0) || (cert_file && strncasecmp (cert_file, "pkcs11:", 7) == 0) || @@ -1528,7 +1528,6 @@ 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); diff --git a/examples/coap-server.c b/examples/coap-server.c index 3f5d03c6..0c0ae5d4 100644 --- a/examples/coap-server.c +++ b/examples/coap-server.c @@ -89,6 +89,11 @@ static char *root_ca_file = NULL; /* List of trusted Root CAs in PEM */ static int use_pem_buf = 0; /* Map these cert/key files into memory to test PEM_BUF logic if set */ static int is_rpk_not_cert = 0; /* Cert is RPK if set */ +/* Used to hold initial PEM_BUF setup */ +static uint8_t *cert_mem_base = NULL; /* certificate and private key in PEM_BUF */ +static uint8_t *key_mem_base = NULL; /* private key in PEM_BUF */ +static uint8_t *ca_mem_base = NULL; /* CA for cert checking in PEM_BUF */ +/* Used for verify_pki_sni_callback PEM_BUF temporary holding */ static uint8_t *cert_mem = NULL; /* certificate and private key in PEM_BUF */ static uint8_t *key_mem = NULL; /* private key in PEM_BUF */ static uint8_t *ca_mem = NULL; /* CA for cert checking in PEM_BUF */ @@ -1835,40 +1840,53 @@ static uint8_t *read_file_mem(const char* file, size_t *length) { return buf; } +static void +update_pki_key(coap_dtls_key_t *dtls_key, const char *key_name, + const char *cert_name, const char *ca_name) {; + memset (dtls_key, 0, sizeof(*dtls_key)); + if ((key_name && strncasecmp (key_name, "pkcs11:", 7) == 0) || + (cert_name && strncasecmp (cert_name, "pkcs11:", 7) == 0) || + (ca_name && strncasecmp (ca_name, "pkcs11:", 7) == 0)) { + dtls_key->key_type = COAP_PKI_KEY_PKCS11; + dtls_key->key.pkcs11.public_cert = cert_name; + dtls_key->key.pkcs11.private_key = key_name ? key_name : cert_name; + dtls_key->key.pkcs11.ca = ca_name; + dtls_key->key.pkcs11.user_pin = pkcs11_pin; + } + else if (!use_pem_buf && !is_rpk_not_cert) { + dtls_key->key_type = COAP_PKI_KEY_PEM; + dtls_key->key.pem.public_cert = cert_name; + dtls_key->key.pem.private_key = key_name ? key_name : cert_name; + dtls_key->key.pem.ca_file = ca_name; + } + else { + /* Map file into memory */ + coap_free(ca_mem); + coap_free(cert_mem); + coap_free(key_mem); + ca_mem = read_file_mem(ca_name, &ca_mem_len); + cert_mem = read_file_mem(cert_name, &cert_mem_len); + key_mem = read_file_mem(key_name, &key_mem_len); + + dtls_key->key_type = COAP_PKI_KEY_PEM_BUF; + dtls_key->key.pem_buf.ca_cert = ca_mem; + dtls_key->key.pem_buf.public_cert = cert_mem; + dtls_key->key.pem_buf.private_key = key_mem ? key_mem : cert_mem; + dtls_key->key.pem_buf.ca_cert_len = ca_mem_len; + dtls_key->key.pem_buf.public_cert_len = cert_mem_len; + dtls_key->key.pem_buf.private_key_len = key_mem ? + key_mem_len : cert_mem_len; + } +} + static coap_dtls_key_t * verify_pki_sni_callback(const char *sni, void *arg COAP_UNUSED ) { static coap_dtls_key_t dtls_key; - /* Preset with the defined keys */ - memset (&dtls_key, 0, sizeof(dtls_key)); - if (!use_pem_buf) { - if ((key_file && strncasecmp (key_file, "pkcs11:", 7) == 0) || - (cert_file && strncasecmp (cert_file, "pkcs11:", 7) == 0) || - (ca_file && strncasecmp (ca_file, "pkcs11:", 7) == 0)) { - dtls_key.key_type = COAP_PKI_KEY_PKCS11; - dtls_key.key.pkcs11.public_cert = cert_file; - dtls_key.key.pkcs11.private_key = key_file ? key_file : cert_file; - dtls_key.key.pkcs11.ca = ca_file; - dtls_key.key.pkcs11.user_pin = pkcs11_pin; - } - else { - dtls_key.key_type = COAP_PKI_KEY_PEM; - dtls_key.key.pem.public_cert = cert_file; - dtls_key.key.pem.private_key = key_file ? key_file : cert_file; - dtls_key.key.pem.ca_file = ca_file; - } - } - else { - dtls_key.key_type = COAP_PKI_KEY_PEM_BUF; - dtls_key.key.pem_buf.ca_cert = ca_mem; - dtls_key.key.pem_buf.public_cert = cert_mem; - dtls_key.key.pem_buf.private_key = cert_mem; - dtls_key.key.pem_buf.ca_cert_len = ca_mem_len; - dtls_key.key.pem_buf.public_cert_len = cert_mem_len; - dtls_key.key.pem_buf.private_key_len = cert_mem_len; - } + update_pki_key(&dtls_key, key_file, cert_file, ca_file); + if (sni[0]) { size_t i; coap_log(LOG_INFO, "SNI '%s' requested\n", sni); @@ -1878,10 +1896,9 @@ verify_pki_sni_callback(const char *sni, coap_log(LOG_INFO, "Switching to using cert '%s' + ca '%s'\n", valid_pki_snis.pki_sni_list[i].new_cert, valid_pki_snis.pki_sni_list[i].new_ca); - dtls_key.key_type = COAP_PKI_KEY_PEM; - dtls_key.key.pem.public_cert = valid_pki_snis.pki_sni_list[i].new_cert; - dtls_key.key.pem.private_key = valid_pki_snis.pki_sni_list[i].new_cert; - dtls_key.key.pem.ca_file = valid_pki_snis.pki_sni_list[i].new_ca; + update_pki_key(&dtls_key, valid_pki_snis.pki_sni_list[i].new_cert, + valid_pki_snis.pki_sni_list[i].new_cert, + valid_pki_snis.pki_sni_list[i].new_ca); break; } } @@ -2008,50 +2025,29 @@ setup_pki(coap_context_t *ctx, coap_dtls_role_t role, char *client_sni) { dtls_pki.check_cert_revocation = 1; dtls_pki.allow_no_crl = 1; dtls_pki.allow_expired_crl = 1; - dtls_pki.validate_cn_call_back = verify_cn_callback; - dtls_pki.cn_call_back_arg = (void*)role; - dtls_pki.validate_sni_call_back = role == COAP_DTLS_ROLE_SERVER ? - verify_pki_sni_callback : NULL; - dtls_pki.sni_call_back_arg = NULL; } - dtls_pki.is_rpk_not_cert = is_rpk_not_cert; + else if (is_rpk_not_cert) { + dtls_pki.verify_peer_cert = verify_peer_cert; + } + dtls_pki.is_rpk_not_cert = is_rpk_not_cert; + dtls_pki.validate_cn_call_back = verify_cn_callback; + dtls_pki.cn_call_back_arg = (void*)role; + dtls_pki.validate_sni_call_back = role == COAP_DTLS_ROLE_SERVER ? + verify_pki_sni_callback : NULL; + dtls_pki.sni_call_back_arg = NULL; if (role == COAP_DTLS_ROLE_CLIENT) { dtls_pki.client_sni = client_sni; } - if ((key_file && strncasecmp (key_file, "pkcs11:", 7) == 0) || - (cert_file && strncasecmp (cert_file, "pkcs11:", 7) == 0) || - (ca_file && strncasecmp (ca_file, "pkcs11:", 7) == 0)) { - dtls_pki.pki_key.key_type = COAP_PKI_KEY_PKCS11; - dtls_pki.pki_key.key.pkcs11.public_cert = cert_file; - dtls_pki.pki_key.key.pkcs11.private_key = key_file ? - key_file : cert_file; - dtls_pki.pki_key.key.pkcs11.ca = ca_file; - dtls_pki.pki_key.key.pkcs11.user_pin = pkcs11_pin; - } - else if (!use_pem_buf && !is_rpk_not_cert) { - dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM; - dtls_pki.pki_key.key.pem.public_cert = cert_file; - dtls_pki.pki_key.key.pem.private_key = key_file ? key_file : cert_file; - dtls_pki.pki_key.key.pem.ca_file = ca_file; - } - else { - /* Map file into memory */ - if (ca_mem == 0 && cert_mem == 0 && key_mem == 0) { - ca_mem = read_file_mem(ca_file, &ca_mem_len); - cert_mem = read_file_mem(cert_file, &cert_mem_len); - key_mem = read_file_mem(key_file, &key_mem_len); - } - dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF; - dtls_pki.pki_key.key.pem_buf.ca_cert = ca_mem; - dtls_pki.pki_key.key.pem_buf.public_cert = cert_mem; - dtls_pki.pki_key.key.pem_buf.private_key = key_mem ? key_mem : cert_mem; - dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_mem_len; - dtls_pki.pki_key.key.pem_buf.public_cert_len = cert_mem_len; - dtls_pki.pki_key.key.pem_buf.private_key_len = key_mem ? - key_mem_len : cert_mem_len; - } + update_pki_key(&dtls_pki.pki_key, key_file, cert_file, ca_file); + /* Need to keep base initialization copies of any COAP_PKI_KEY_PEM_BUF */ + ca_mem_base = ca_mem; + cert_mem_base = cert_mem; + key_mem_base = key_mem; + ca_mem = NULL; + cert_mem = NULL; + key_mem = NULL; return &dtls_pki; } @@ -2678,7 +2674,6 @@ 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': verify_peer_cert = 0; @@ -2850,6 +2845,9 @@ main(int argc, char **argv) { coap_free(ca_mem); coap_free(cert_mem); coap_free(key_mem); + coap_free(ca_mem_base); + coap_free(cert_mem_base); + coap_free(key_mem_base); for (i = 0; i < valid_psk_snis.count; i++) { free(valid_psk_snis.psk_sni_list[i].sni_match); coap_delete_bin_const(valid_psk_snis.psk_sni_list[i].new_hint); diff --git a/src/coap_gnutls.c b/src/coap_gnutls.c index f8f22a37..e875da12 100644 --- a/src/coap_gnutls.c +++ b/src/coap_gnutls.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #if (GNUTLS_VERSION_NUMBER >= 0x030606) #define COAP_GNUTLS_KEY_RPK GNUTLS_KEY_DIGITAL_SIGNATURE | \ @@ -629,8 +630,6 @@ static gnutls_certificate_type_t get_san_or_cn(gnutls_session_t g_session, #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; cert_info->san_or_cn = NULL; @@ -640,6 +639,9 @@ static gnutls_certificate_type_t get_san_or_cn(gnutls_session_t g_session, return GNUTLS_CRT_UNKNOWN; } + if (cert_info->certificate_type != GNUTLS_CRT_X509) + return cert_info->certificate_type; + G_CHECK(gnutls_x509_crt_init(&cert), "gnutls_x509_crt_init"); /* Interested only in first cert in chain */ @@ -700,6 +702,42 @@ fail: cert_info.san_or_cn : "?") #endif /* GNUTLS_VERSION_NUMBER < 0x030606 */ +#if (GNUTLS_VERSION_NUMBER >= 0x030606) +static int +check_rpk_cert(coap_gnutls_context_t *g_context, + coap_gnutls_certificate_info_t *cert_info, + coap_session_t *c_session) { + int ret; + + if (g_context->setup_data.validate_cn_call_back) { + gnutls_pcert_st pcert; + uint8_t der[2048]; + size_t size; + + G_CHECK(gnutls_pcert_import_rawpk_raw(&pcert, &cert_info->cert_list[0], + GNUTLS_X509_FMT_DER, 0, 0), + "gnutls_pcert_import_rawpk_raw"); + + size = sizeof(der); + G_CHECK(gnutls_pubkey_export(pcert.pubkey, GNUTLS_X509_FMT_DER, der, &size), + "gnutls_pubkey_export"); + gnutls_pcert_deinit(&pcert); + if (!g_context->setup_data.validate_cn_call_back(COAP_DTLS_RPK_CERT_CN, + der, + size, + c_session, + 0, + 1, + g_context->setup_data.cn_call_back_arg)) { + return 0; + } + } + return 1; +fail: + return 0; +} +#endif /* >= 3.6.6 */ + /* * return 0 failed * 1 passed @@ -722,9 +760,16 @@ static int cert_verify_gnutls(gnutls_session_t g_session) cert_type = get_san_or_cn(g_session, &cert_info); #if (GNUTLS_VERSION_NUMBER >= 0x030606) if (cert_type == GNUTLS_CRT_RAW) - goto finish; + if (!check_rpk_cert(g_context, &cert_info, c_session)) { + alert = GNUTLS_A_ACCESS_DENIED; + goto fail; + } + return 1; #endif /* >= 3.6.6 */ + if (cert_info.cert_list_size == 0 && !g_context->setup_data.verify_peer_cert) + return 1; + G_CHECK(gnutls_certificate_verify_peers(g_session, NULL, 0, &status), "gnutls_certificate_verify_peers"); @@ -859,9 +904,6 @@ static int cert_verify_gnutls(gnutls_session_t g_session) } } -#if (GNUTLS_VERSION_NUMBER >= 0x030606) -finish: -#endif /* >= 3.6.6 */ if (cert_info.san_or_cn) gnutls_free(cert_info.san_or_cn); @@ -1841,6 +1883,10 @@ setup_server_ssl_session(coap_session_t *c_session, coap_gnutls_env_t *g_env) gnutls_certificate_server_set_request(g_env->g_session, GNUTLS_CERT_REQUIRE); } + else if (setup_data->is_rpk_not_cert) { + gnutls_certificate_server_set_request(g_env->g_session, + GNUTLS_CERT_REQUEST); + } else { gnutls_certificate_server_set_request(g_env->g_session, GNUTLS_CERT_IGNORE);