Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,15 @@ set(CMAKE_MACOSX_RPATH TRUE)
# micro version is changed with a set of small changes or bugfixes anywhere in the project.
set(LIBNETCONF2_MAJOR_VERSION 4)
set(LIBNETCONF2_MINOR_VERSION 6)
set(LIBNETCONF2_MICRO_VERSION 5)
set(LIBNETCONF2_MICRO_VERSION 6)
set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION})

# Version of the library
# Major version is changed with every backward non-compatible API/ABI change in the library, minor version changes
# with backward compatible change and micro version is connected with any internal change of the library.
set(LIBNETCONF2_MAJOR_SOVERSION 5)
set(LIBNETCONF2_MINOR_SOVERSION 5)
set(LIBNETCONF2_MICRO_SOVERSION 3)
set(LIBNETCONF2_MICRO_SOVERSION 4)
set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION})
set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION})

Expand Down
76 changes: 68 additions & 8 deletions src/session_mbedtls.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ nc_server_tls_dn2str(const mbedtls_x509_name *dn)
NC_CHECK_ERRMEM_RET(!str, NULL);

while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) {
/* the error is also returned for any attribute value longer than MBEDTLS_X509_MAX_DN_NAME_SIZE,
* which no buffer size can fix, so limit the growth */
if (len >= NC_TLS_DN_STR_MAX_LEN) {
break;
}

len <<= 1;
str = nc_realloc(str, len);
NC_CHECK_ERRMEM_RET(!str, NULL);
Expand Down Expand Up @@ -888,6 +894,52 @@ nc_server_tls_get_issuer_wrap(void *cert)
return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->issuer));
}

int
nc_server_tls_get_cn_wrap(void *cert, char **cn)
{
const mbedtls_x509_name *iter, *cn_attr = NULL;

*cn = NULL;

for (iter = &((mbedtls_x509_crt *)cert)->subject; iter; iter = iter->next) {
if (!iter->oid.p) {
continue;
}

/* skip attributes other than commonName */
if (MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &iter->oid)) {
continue;
}

if (cn_attr) {
/* the name would be ambiguous */
WRN(NULL, "Certificate includes multiple commonName fields.");
return 1;
}
cn_attr = iter;
}
if (!cn_attr) {
WRN(NULL, "Certificate does not include the commonName field.");
return 1;
}

/* the name must be in UTF-8 (RFC 6353), so only accept string types that are UTF-8 compatible */
if ((cn_attr->val.tag != MBEDTLS_ASN1_UTF8_STRING) && (cn_attr->val.tag != MBEDTLS_ASN1_PRINTABLE_STRING) &&
(cn_attr->val.tag != MBEDTLS_ASN1_IA5_STRING)) {
WRN(NULL, "Certificate commonName uses an unsupported string type.");
return 1;
}
if (!cn_attr->val.len) {
WRN(NULL, "Certificate commonName is empty.");
return 1;
}

*cn = strndup((const char *)cn_attr->val.p, cn_attr->val.len);
NC_CHECK_ERRMEM_RET(!*cn, -1);

return 0;
}

void *
nc_tls_get_sans_wrap(void *cert)
{
Expand Down Expand Up @@ -922,7 +974,7 @@ nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN
int i, rc, ret = 0;
mbedtls_x509_sequence *iter;
mbedtls_x509_subject_alternative_name san = {0};
const mbedtls_x509_buf *ip;
const mbedtls_x509_buf *ip, *str;

*san_value = NULL;
*san_type = NC_TLS_CTN_UNKNOWN;
Expand All @@ -942,20 +994,28 @@ nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN
/* get its type and value */
switch (san.type) {
case MBEDTLS_X509_SAN_DNS_NAME:
*san_type = NC_TLS_CTN_SAN_DNS_NAME;
*san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len);
NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup);
break;
case MBEDTLS_X509_SAN_RFC822_NAME:
*san_type = NC_TLS_CTN_SAN_RFC822_NAME;
*san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len);
str = &san.san.unstructured_name;
if (!str->len) {
WRN(NULL, "SAN value is empty, skipping.");
ret = 1;
goto cleanup;
} else if (memchr(str->p, '\0', str->len)) {
/* the value would get truncated */
WRN(NULL, "SAN value includes a NUL character, skipping.");
ret = 1;
goto cleanup;
}

*san_type = (san.type == MBEDTLS_X509_SAN_DNS_NAME) ? NC_TLS_CTN_SAN_DNS_NAME : NC_TLS_CTN_SAN_RFC822_NAME;
*san_value = strndup((const char *)str->p, str->len);
NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup);
break;
case MBEDTLS_X509_SAN_IP_ADDRESS:
*san_type = NC_TLS_CTN_SAN_IP_ADDRESS;
ip = &san.san.unstructured_name;
if (ip->len == 4) {
rc = asprintf(san_value, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]) == -1;
rc = asprintf(san_value, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]);
NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup);
} else if (ip->len == 16) {
rc = asprintf(san_value, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
Expand Down
69 changes: 62 additions & 7 deletions src/session_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,51 @@ nc_server_tls_get_issuer_wrap(void *cert)
return X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
}

int
nc_server_tls_get_cn_wrap(void *cert, char **cn)
{
int rc = 0, idx, len;
X509_NAME *subject;
X509_NAME_ENTRY *entry;
unsigned char *utf8 = NULL;

*cn = NULL;

/* find the index of the first commonName attribute in the subject, -1 means searching from the start */
subject = X509_get_subject_name(cert);
idx = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
if (idx < 0) {
WRN(NULL, "Certificate does not include the commonName field.");
return 1;
}

/* search again after the found attribute, another commonName would make the name ambiguous */
if (X509_NAME_get_index_by_NID(subject, NID_commonName, idx) > -1) {
WRN(NULL, "Certificate includes multiple commonName fields.");
return 1;
}

/* the name must be in UTF-8 (RFC 6353), so convert the value from whatever string type it is */
entry = X509_NAME_get_entry(subject, idx);
len = ASN1_STRING_to_UTF8(&utf8, X509_NAME_ENTRY_get_data(entry));
if (len < 0) {
WRN(NULL, "Certificate commonName could not be converted to UTF-8 (%s).", ERR_reason_error_string(ERR_get_error()));
return 1;
}
if (!len) {
WRN(NULL, "Certificate commonName is empty.");
rc = 1;
goto cleanup;
}

*cn = strdup((char *)utf8);
NC_CHECK_ERRMEM_GOTO(!*cn, rc = -1, cleanup);

cleanup:
OPENSSL_free(utf8);
return rc;
}

void *
nc_tls_get_sans_wrap(void *cert)
{
Expand All @@ -618,8 +663,9 @@ nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN
{
int ret = 0;
GENERAL_NAME *san;
int ip_length;
int ip_length, str_len;
const unsigned char *ip_data;
ASN1_IA5STRING *str;

*san_value = NULL;
*san_type = NC_TLS_CTN_UNKNOWN;
Expand All @@ -633,13 +679,22 @@ nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN
/* get its type and value */
switch (san->type) {
case GEN_EMAIL:
*san_type = NC_TLS_CTN_SAN_RFC822_NAME;
*san_value = strdup((char *)ASN1_STRING_get0_data(san->d.rfc822Name));
NC_CHECK_ERRMEM_RET(!*san_value, -1);
break;
case GEN_DNS:
*san_type = NC_TLS_CTN_SAN_DNS_NAME;
*san_value = strdup((char *)ASN1_STRING_get0_data(san->d.dNSName));
str = (san->type == GEN_EMAIL) ? san->d.rfc822Name : san->d.dNSName;
str_len = ASN1_STRING_length(str);
if (!str_len) {
WRN(NULL, "SAN value is empty, skipping.");
ret = 1;
break;
} else if (memchr(ASN1_STRING_get0_data(str), '\0', str_len)) {
/* the value would get truncated */
WRN(NULL, "SAN value includes a NUL character, skipping.");
ret = 1;
break;
}

*san_type = (san->type == GEN_EMAIL) ? NC_TLS_CTN_SAN_RFC822_NAME : NC_TLS_CTN_SAN_DNS_NAME;
*san_value = strndup((char *)ASN1_STRING_get0_data(str), str_len);
NC_CHECK_ERRMEM_RET(!*san_value, -1);
break;
case GEN_IPADD:
Expand Down
5 changes: 5 additions & 0 deletions src/session_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ extern struct nc_server_opts server_opts;
*/
#define NC_TLS_KEYLOGFILE_ENV "SSLKEYLOGFILE"

/**
* Maximum size of a buffer for a certificate DN string (MbedTLS).
*/
#define NC_TLS_DN_STR_MAX_LEN 65536

#define NC_VERSION_10_ENDTAG "]]>]]>"
#define NC_VERSION_10_ENDTAG_LEN 6

Expand Down
31 changes: 3 additions & 28 deletions src/session_server_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,41 +278,16 @@ nc_server_tls_sha512(void *cert)
static int
nc_server_tls_get_username(void *cert, struct nc_ctn *ctn, char **username)
{
char *subject, *cn, *san_value = NULL, rdn_separator;
char *san_value = NULL;
void *sans;
int i, nsans = 0, rc;
NC_TLS_CTN_MAPTYPE san_type = 0;

#ifdef HAVE_LIBMEDTLS
rdn_separator = ',';
#else
rdn_separator = '/';
#endif

if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
*username = strdup(ctn->name);
NC_CHECK_ERRMEM_RET(!*username, -1);
} else if (ctn->map_type == NC_TLS_CTN_COMMON_NAME) {
subject = nc_server_tls_get_subject_wrap(cert);
if (!subject) {
return -1;
}

cn = strstr(subject, "CN=");
if (!cn) {
WRN(NULL, "Certificate does not include the commonName field.");
free(subject);
return 1;
}

/* skip "CN=" */
cn += 3;
if (strchr(cn, rdn_separator)) {
*strchr(cn, rdn_separator) = '\0';
}
*username = strdup(cn);
free(subject);
NC_CHECK_ERRMEM_RET(!*username, -1);
return nc_server_tls_get_cn_wrap(cert, username);
} else {
sans = nc_tls_get_sans_wrap(cert);
if (!sans) {
Expand All @@ -329,7 +304,7 @@ nc_server_tls_get_username(void *cert, struct nc_ctn *ctn, char **username)
return -1;
}

/* got a type that we dont care about */
/* got a type that we dont care about or an unusable value */
continue;
}

Expand Down
15 changes: 14 additions & 1 deletion src/session_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,17 @@ char * nc_server_tls_get_subject_wrap(void *cert);
*/
char * nc_server_tls_get_issuer_wrap(void *cert);

/**
* @brief Get the commonName of a certificate.
*
* @param[in] cert Certificate to get the commonName from.
* @param[out] cn Parsed commonName.
* @return 0 on success.
* @return 1 if no usable commonName was found.
* @return -1 on error.
*/
int nc_server_tls_get_cn_wrap(void *cert, char **cn);

/**
* @brief Get the Subject Alternative Names of the certificate.
*
Expand Down Expand Up @@ -299,7 +310,9 @@ int nc_tls_get_num_sans_wrap(void *sans);
* @param[in] idx Index of the SAN.
* @param[out] san_value SAN value.
* @param[out] san_type SAN type.
* @return 0 on success, non-zero on fail.
* @return 0 on success.
* @return 1 if the SAN is of an unsupported type or its value cannot be used.
* @return -1 on error.
*/
int nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type);

Expand Down
27 changes: 27 additions & 0 deletions tests/data/ctn/cn_ou_inject.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEmDCCA4CgAwIBAgIUARWttiJibIoVz3/GNzxlnjL0xnMwDQYJKoZIhvcNAQEL
BQAwYzELMAkGA1UEBhMCQ1oxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAcM
BEJybm8xDzANBgNVBAoMBkNFU05FVDEMMAoGA1UECwwDVE1DMREwDwYDVQQDDAhz
ZXJ2ZXJjYTAeFw0yNjEwMTYxMDQzNDNaFw0zNjEwMTMxMDQzNDNaMDIxCzAJBgNV
BAYTAkNaMRAwDgYDVQQLDAdDTj1yb290MREwDwYDVQQDDAhvcGVyYXRvcjCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL6So+oMv0ZtaapVCGniqVri9iji
QW6HYcbjxvtEun/iX+C6KTge+rZoNUUP2wFyzcV5BuOgsfVDh2hSvyMbpXAmx+i8
TlIPdPx1UuzZ22lbQ7JdNxxvQj2U5UMzPp/ohQF7+bm+Txc1t1QgjQ0nzLV1DnVU
fd7vzgHO7s9J1oxoVpzh+TDyQkD0vGRaS/YUMULsgFxWNGC1v/XkTA4Mfzj07r5U
8SBfmB2jM2PlsvjmsoMG+/HQqMPt0UEYAgdtKOaOnscdp19gqq+1Ccxvzd9FwrrC
iHE0/e/LXfAFO1I6W86DXoZv7ZkHQ6vvCA7DX1m8Qhw2CzA/CEd9lA1xgw1UsK7T
YFLPB1jTQgSo0zQnkH9b/8okXe4UX9mo/sPbA7vDyPlQvaWm7EXtisZ5w1PUqvuC
Ad2TNwuV6SQ27vA1YoBmUnr4pu6fw+pltqYZcsbZSvVpkOTIrIP2fIzOxGd4CXE7
4dtCEuiOKZRuwyb7CAvz3NyEtiQv8NKawmWvIDiqG/Mdpo40HDgNXlSFJssOlm1d
V+uf4wCbyEump/6BQJOGwU3yQOn2H1OD2/TVqL2MZv3jUeQW0ZhRYdLl2tLPdkud
IjqLX/Ap1uz2ez1ioMyqrZzfTm/mHu1ow3RvjQq4ZIgwTgrhw6x18KmsVgZlCAqN
pp1DCuybiTUmXuQzAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD
AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMB0GA1UdDgQWBBRcHzwAR0VfryCitovR
SC1Cq4layzAfBgNVHSMEGDAWgBTrScnirci1x9o44GMlKpp0A+IyDzANBgkqhkiG
9w0BAQsFAAOCAQEARgeByaWSrw4hMvtzz70Y35V8MDOgF3XWQwvEsVKxB3AWQKb6
89M58cwf6R1CbNB3Gq2HQBmk/F6WdX2zqlZnGWuWWoIXhIRLHYfo4rKlCjDxgprG
CFWsBwt6tC09bI3Q6kjFdkN5IR/KOzLciBPLSdY8/NivQymA4T/2tLWlJnSXcm+L
cW/kBs3jZ63pqDbPblz8LKR9hA2gVzQywcR7u6cZO7Ys1B7RoqsKfZBLfGoTyxLn
DwX8zHArbvrwxD041eO3Q+kV/ZsXzThJ15JeVpsOpHNd5UWHNgifVs0xU64Lammz
Ma1XE91LfZNWQgKgxILKD49UXKQH2zDOkbK9HQ==
-----END CERTIFICATE-----
26 changes: 26 additions & 0 deletions tests/data/ctn/san_nul.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEcTCCA1mgAwIBAgIUM8HxiglexMFAeSvCsAJa7MsgLJMwDQYJKoZIhvcNAQEL
BQAwYzELMAkGA1UEBhMCQ1oxEzARBgNVBAgMClNvbWUtU3RhdGUxDTALBgNVBAcM
BEJybm8xDzANBgNVBAoMBkNFU05FVDEMMAoGA1UECwwDVE1DMREwDwYDVQQDDAhz
ZXJ2ZXJjYTAeFw0yNjEwMTYxMTI4MTlaFw0zNjEwMTMxMTI4MTlaMCAxCzAJBgNV
BAYTAkNaMREwDwYDVQQDDAhvcGVyYXRvcjCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAL6So+oMv0ZtaapVCGniqVri9ijiQW6HYcbjxvtEun/iX+C6KTge
+rZoNUUP2wFyzcV5BuOgsfVDh2hSvyMbpXAmx+i8TlIPdPx1UuzZ22lbQ7JdNxxv
Qj2U5UMzPp/ohQF7+bm+Txc1t1QgjQ0nzLV1DnVUfd7vzgHO7s9J1oxoVpzh+TDy
QkD0vGRaS/YUMULsgFxWNGC1v/XkTA4Mfzj07r5U8SBfmB2jM2PlsvjmsoMG+/HQ
qMPt0UEYAgdtKOaOnscdp19gqq+1Ccxvzd9FwrrCiHE0/e/LXfAFO1I6W86DXoZv
7ZkHQ6vvCA7DX1m8Qhw2CzA/CEd9lA1xgw1UsK7TYFLPB1jTQgSo0zQnkH9b/8ok
Xe4UX9mo/sPbA7vDyPlQvaWm7EXtisZ5w1PUqvuCAd2TNwuV6SQ27vA1YoBmUnr4
pu6fw+pltqYZcsbZSvVpkOTIrIP2fIzOxGd4CXE74dtCEuiOKZRuwyb7CAvz3NyE
tiQv8NKawmWvIDiqG/Mdpo40HDgNXlSFJssOlm1dV+uf4wCbyEump/6BQJOGwU3y
QOn2H1OD2/TVqL2MZv3jUeQW0ZhRYdLl2tLPdkudIjqLX/Ap1uz2ez1ioMyqrZzf
Tm/mHu1ow3RvjQq4ZIgwTgrhw6x18KmsVgZlCAqNpp1DCuybiTUmXuQzAgMBAAGj
YDBeMBwGA1UdEQQVMBOBEXJvb3QAQGV4YW1wbGUuY29tMB0GA1UdDgQWBBRcHzwA
R0VfryCitovRSC1Cq4layzAfBgNVHSMEGDAWgBTrScnirci1x9o44GMlKpp0A+Iy
DzANBgkqhkiG9w0BAQsFAAOCAQEAZ0sQEwJTTpwx3LaRfAb0Uq5yb2S18j16fdMj
UL3TZV2C7olUYiYHSjnb56vcZXaajb3dgVNgtjh3Hj7yWY3layzVtoF0AGMMkJhK
QVdEiGbaRsfsw4f5qafQbAg0bnvuzkoS4zArb8b2XOgIsElk/+9EAbGWkQmbRGFf
JIq2g347TG6c2ZcdmVB5uHOXTet9tHo6Afqd9Y7WCtHMAot1qgxdcWFkUVz29JOD
c98Awm3wk7Tylm1O1aZEC0ol31Tz9McA+4B+1t16ltAbnX0ZoVz9cj9cQkXyavmz
Qt4lLs8CmBNOLapnyvfiwUQFImYOsTzyglbUcvs+4gtrW3Qjwg==
-----END CERTIFICATE-----
Loading
Loading