Skip to content

Commit c201a91

Browse files
committed
X.509: add check for DN/SAN
Signed-off-by: Stephan Mueller <smueller@chronox.de>
1 parent 588a367 commit c201a91

File tree

8 files changed

+336
-127
lines changed

8 files changed

+336
-127
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ Changes 1.7.0-prerelease
2323

2424
* AVX2 / AVX512 / CLMUL detection: Follow Intel instructions on detecting support. Also, ensure that code compiled with acceleration support is always invoked by non-accelerated code which has the check for acceleration support.
2525

26+
* X.509: add lc_x509_policy_cert_subject_match API
27+
2628
Changes 1.6.0
2729
* ASN.1: use stack for small generator for small use cases
2830

apps/src/lc_x509_generator.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ struct x509_generator_opts {
4949
struct lc_x509_key_data signer_key_data;
5050
struct x509_checker_options checker_opts;
5151
struct lc_pkcs8_message pkcs8;
52-
uint8_t ipaddr[16];
5352
uint8_t *raw_skid;
5453
size_t raw_skid_size;
5554
uint8_t *raw_akid;
@@ -317,12 +316,13 @@ static int x509_enc_san_dns(struct x509_generator_opts *opts,
317316
static int x509_enc_san_ip(struct x509_generator_opts *opts, char *opt_optarg)
318317
{
319318
struct lc_x509_certificate *cert = &opts->cert;
320-
size_t ip_len = sizeof(opts->ipaddr);
319+
uint8_t ipaddr[16];
320+
size_t ip_len = sizeof(ipaddr);
321321
int ret;
322322

323-
CKINT(lc_x509_enc_san_ip(cert, opt_optarg, opts->ipaddr, &ip_len));
323+
CKINT(lc_x509_enc_san_ip(opt_optarg, ipaddr, &ip_len));
324324

325-
CKINT(lc_x509_cert_set_san_ip(cert, opts->ipaddr, ip_len));
325+
CKINT(lc_x509_cert_set_san_ip(cert, ipaddr, ip_len));
326326

327327
out:
328328
return ret;

apps/src/lc_x509_generator_checker.c

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,17 @@ int apply_checks_x509(const struct lc_x509_certificate *x509,
171171
}
172172

173173
if (parsed_opts->issuer_cn) {
174-
if (strncmp(x509->issuer, parsed_opts->issuer_cn,
175-
sizeof(x509->issuer))) {
174+
struct lc_x509_certificate_name search_name = {
175+
.cn = {
176+
.value = parsed_opts->issuer_cn,
177+
.size = (uint8_t)strlen(parsed_opts->issuer_cn),
178+
}
179+
};
180+
181+
if (lc_x509_policy_cert_subject_match(
182+
x509, &search_name,
183+
lc_x509_policy_cert_subject_match_issuer_only) ==
184+
LC_X509_POL_FALSE) {
176185
printf("Issuers mismatch, expected %s, actual %s\n",
177186
parsed_opts->issuer_cn, x509->issuer);
178187
return -EINVAL;
@@ -181,8 +190,17 @@ int apply_checks_x509(const struct lc_x509_certificate *x509,
181190
}
182191
}
183192
if (parsed_opts->subject_cn) {
184-
if (strncmp(x509->subject, parsed_opts->subject_cn,
185-
sizeof(x509->subject))) {
193+
struct lc_x509_certificate_name search_name = {
194+
.cn = {
195+
.value = parsed_opts->subject_cn,
196+
.size = (uint8_t)strlen(parsed_opts->subject_cn),
197+
}
198+
};
199+
200+
if (lc_x509_policy_cert_subject_match(
201+
x509, &search_name,
202+
lc_x509_policy_cert_subject_match_dn_only) ==
203+
LC_X509_POL_FALSE) {
186204
printf("Subject mismatch, expected %s, actual %s\n",
187205
parsed_opts->subject_cn, x509->subject);
188206
return -EINVAL;
@@ -227,41 +245,36 @@ int apply_checks_x509(const struct lc_x509_certificate *x509,
227245
}
228246

229247
if (parsed_opts->san_dns) {
230-
size_t exp_len = strlen(parsed_opts->san_dns);
231-
232-
if (exp_len != x509->san_dns_len) {
233-
printf("SAN DNS: lengths differ (expected %zu, actual %zu)\n",
234-
exp_len, x509->san_dns_len);
235-
return -EINVAL;
236-
}
237-
238-
if (memcmp(parsed_opts->san_dns, x509->san_dns, exp_len)) {
239-
char buf[128];
240-
241-
memcpy(buf, x509->san_dns,
242-
min_size(sizeof(buf), x509->san_dns_len));
248+
struct lc_x509_certificate_name search_name = {
249+
.cn = {
250+
.value = parsed_opts->subject_cn,
251+
.size = (uint8_t)strlen(parsed_opts->subject_cn),
252+
}
253+
};
243254

255+
if (lc_x509_policy_cert_subject_match(
256+
x509, &search_name,
257+
lc_x509_policy_cert_subject_match_san_dns_only) ==
258+
LC_X509_POL_FALSE) {
244259
printf("SAN DNS: names mismatch (expected %s, actual %s)\n",
245-
parsed_opts->san_dns, buf);
260+
parsed_opts->san_dns, x509->san_dns);
246261
return -EINVAL;
247262
} else {
248263
printf("SAN DNS match\n");
249264
}
250265
}
251266
if (parsed_opts->san_ip) {
252-
uint8_t exp_ip_bin[16];
253-
size_t exp_len = strlen(parsed_opts->san_ip);
254-
255-
hex2bin(parsed_opts->san_ip, exp_len, exp_ip_bin,
256-
sizeof(exp_ip_bin));
257-
258-
if (exp_len / 2 != x509->san_ip_len) {
259-
printf("SAN IP: lengths differ (expected %zu, actual %zu)\n",
260-
exp_len, x509->san_ip_len);
261-
return -EINVAL;
262-
}
267+
struct lc_x509_certificate_name search_name = {
268+
.cn = {
269+
.value = parsed_opts->san_ip,
270+
.size = (uint8_t)strlen(parsed_opts->san_ip),
271+
}
272+
};
263273

264-
if (memcmp(exp_ip_bin, x509->san_ip, x509->san_ip_len)) {
274+
if (lc_x509_policy_cert_subject_match(
275+
x509, &search_name,
276+
lc_x509_policy_cert_subject_match_issuer_only) ==
277+
LC_X509_POL_FALSE) {
265278
char buf[33] = { 0 };
266279

267280
bin2hex(x509->san_ip, x509->san_ip_len, buf,

asn1/api/lc_x509_generator.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -325,20 +325,6 @@ int lc_x509_cert_set_san_dns(struct lc_x509_certificate *cert,
325325
int lc_x509_cert_set_san_ip(struct lc_x509_certificate *cert,
326326
const uint8_t *san_ip, size_t san_ip_len);
327327

328-
/**
329-
* @ingroup X509Gen
330-
* @brief Helper to convert the human IP address value into binary form
331-
*
332-
* @param [in] cert Certificate data structure to be filled with the data
333-
* @param [in] ip_name Caller-provided buffer to fill with human-readable form
334-
* @param [out] ip Caller-provided buffer of binary representation of IP address
335-
* @param [in] ip_len Length of the IP address buffer
336-
*
337-
* @return 0 on success or < 0 on error
338-
*/
339-
int lc_x509_enc_san_ip(struct lc_x509_certificate *cert, char *ip_name,
340-
uint8_t *ip, size_t *ip_len);
341-
342328
/**
343329
* @ingroup X509Gen
344330
* @brief Set the SKID value

asn1/api/lc_x509_parser.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,18 @@ int lc_x509_cert_get_keyusage_val(const struct lc_x509_certificate *cert,
544544
int lc_x509_cert_get_san_dns(const struct lc_x509_certificate *cert,
545545
const char **san_dns_name, size_t *san_dns_len);
546546

547+
/**
548+
* @ingroup X509
549+
* @brief Helper to convert the human IP address value into binary form
550+
*
551+
* @param [in] ip_name Caller-provided buffer to fill with human-readable form
552+
* @param [out] ip Caller-provided buffer of binary representation of IP address
553+
* @param [in] ip_len Length of the IP address buffer
554+
*
555+
* @return 0 on success or < 0 on error
556+
*/
557+
int lc_x509_enc_san_ip(const char *ip_name, uint8_t *ip, size_t *ip_len);
558+
547559
/**
548560
* @ingroup X509
549561
* @brief Get the SAN IP value
@@ -891,6 +903,10 @@ typedef int lc_x509_pol_ret_t /* __attribute__((warn_unused_result)) */;
891903
* @ingroup X509
892904
* @brief Is the given certificate a CA certificate (root or intermediate)?
893905
*
906+
* \note The verification of the signature and the general constraints of the
907+
* certificate is not performed here but must be invoked with
908+
* \p lc_x509_policy_verify_cert.
909+
*
894910
* @param [in] cert Reference to the certificate
895911
*
896912
* @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE
@@ -924,6 +940,10 @@ lc_x509_policy_is_selfsigned(const struct lc_x509_certificate *cert);
924940
* @ingroup X509
925941
* @brief Is the given certificate a root CA certificate?
926942
*
943+
* \note The verification of the signature and the general constraints of the
944+
* certificate is not performed here but must be invoked with
945+
* \p lc_x509_policy_verify_cert.
946+
*
927947
* @param [in] cert Reference to the certificate
928948
*
929949
* @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE
@@ -1039,6 +1059,49 @@ int lc_x509_policy_verify_cert(const struct lc_public_key *pkey,
10391059
const struct lc_x509_certificate *cert,
10401060
uint64_t flags);
10411061

1062+
enum lc_x509_policy_cert_subject_match_flag {
1063+
/** Match DN or SAN IP or DNS - success if one matches */
1064+
lc_x509_policy_cert_subject_match_dn_and_san,
1065+
/** Match DN only */
1066+
lc_x509_policy_cert_subject_match_dn_only,
1067+
/** Match SAN IP only */
1068+
lc_x509_policy_cert_subject_match_san_ip_only,
1069+
/** Match SAN DNS only */
1070+
lc_x509_policy_cert_subject_match_san_dns_only,
1071+
/** Match SAN name segments */
1072+
lc_x509_policy_cert_subject_match_san_name_only,
1073+
/** Match issuer only */
1074+
lc_x509_policy_cert_subject_match_issuer_only,
1075+
};
1076+
1077+
/**
1078+
* @ingroup X509
1079+
* @brief Match certificate against a provided name
1080+
*
1081+
* This function matches the \p search_name information against the certificate
1082+
* DN and/or the SAN or the issuer as specified with the \p flag field. Only the
1083+
* name components specified with \p search_name are checked. If \p search_name
1084+
* does not contain a component, but the certificate has it, it will be matched.
1085+
*
1086+
* For example: The certificate has a DN with cn="aaa", c="bbb" and the search
1087+
* contains cn="aaa" with all other components empty, it will match the
1088+
* certificate. The string matching is an exact match (string length and
1089+
* content must be identical).
1090+
*
1091+
* \note The SAN DNS and IP search string must be provided in
1092+
* \p search_name->cn. For the IP, the unencoded IP is to be provided.
1093+
*
1094+
* @param [in] cert Reference to the certificate to be validated
1095+
* @param [in] search_name Search information
1096+
* @param [in] flags Flags for the search process
1097+
*
1098+
* @return 0 on success, < 0 on error
1099+
*/
1100+
lc_x509_pol_ret_t lc_x509_policy_cert_subject_match(
1101+
const struct lc_x509_certificate *cert,
1102+
const struct lc_x509_certificate_name *search_name,
1103+
enum lc_x509_policy_cert_subject_match_flag flag);
1104+
10421105
#ifdef __cplusplus
10431106
}
10441107
#endif

asn1/src/x509_cert_generator_set_data.c

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -234,64 +234,6 @@ LC_INTERFACE_FUNCTION(int, lc_x509_cert_set_san_dns,
234234
return ret;
235235
}
236236

237-
LC_INTERFACE_FUNCTION(int, lc_x509_enc_san_ip, struct lc_x509_certificate *cert,
238-
char *ip_name, uint8_t *ip, size_t *ip_len)
239-
{
240-
/*
241-
* EFI does not have support for strstr, strtok_r and strtoul, so
242-
* we simply do not compile this function. As this is a rarely used
243-
* helper, we simply do not provide this function.
244-
*/
245-
#if defined(LC_EFI) || defined(LINUX_KERNEL)
246-
int ret;
247-
248-
(void)cert;
249-
(void)ip_name;
250-
(void)ip;
251-
(void)ip_len;
252-
253-
CKRET(1, -EOPNOTSUPP);
254-
255-
out:
256-
return ret;
257-
#else
258-
unsigned long val;
259-
char *saveptr = NULL;
260-
char *res = NULL;
261-
const char *tok = ".";
262-
unsigned int i, upper = 4;
263-
int ret = 0, base = 10;
264-
265-
CKNULL(cert, -EINVAL);
266-
CKNULL(ip_name, -EINVAL);
267-
CKNULL(ip, -EINVAL);
268-
CKNULL(ip_len, -EINVAL);
269-
270-
/* Check for IPv6 */
271-
if (strstr(ip_name, ":")) {
272-
tok = ":";
273-
upper = 16;
274-
base = 16;
275-
}
276-
277-
CKRET(*ip_len < upper, -EOVERFLOW);
278-
279-
res = strtok_r(ip_name, tok, &saveptr);
280-
for (i = 0; i < upper; i++) {
281-
CKNULL(res, -EINVAL);
282-
val = strtoul(res, NULL, base);
283-
CKRET(val > 255, -EINVAL);
284-
ip[i] = (uint8_t)val;
285-
res = strtok_r(NULL, tok, &saveptr);
286-
}
287-
288-
*ip_len = i;
289-
290-
out:
291-
return ret;
292-
#endif
293-
}
294-
295237
LC_INTERFACE_FUNCTION(int, lc_x509_cert_set_san_ip,
296238
struct lc_x509_certificate *cert, const uint8_t *san_ip,
297239
size_t san_ip_len)

asn1/src/x509_cert_parser.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,3 +1390,60 @@ LC_INTERFACE_FUNCTION(void, lc_x509_keys_zero_free,
13901390
lc_x509_keys_zero(keys);
13911391
lc_free(keys);
13921392
}
1393+
1394+
LC_INTERFACE_FUNCTION(int, lc_x509_enc_san_ip, const char *ip_name, uint8_t *ip,
1395+
size_t *ip_len)
1396+
{
1397+
/*
1398+
* EFI does not have support for strstr, strtok_r and strtoul, so
1399+
* we simply do not compile this function. As this is a rarely used
1400+
* helper, we simply do not provide this function.
1401+
*/
1402+
#if defined(LC_EFI) || defined(LINUX_KERNEL)
1403+
int ret;
1404+
1405+
(void)ip_name;
1406+
(void)ip;
1407+
(void)ip_len;
1408+
1409+
CKRET(1, -EOPNOTSUPP);
1410+
1411+
out:
1412+
return ret;
1413+
#else
1414+
unsigned long val;
1415+
char *saveptr = NULL;
1416+
char *res = NULL;
1417+
const char *tok = ".";
1418+
unsigned int i, upper = 4;
1419+
int ret = 0, base = 10;
1420+
1421+
CKNULL(ip_name, -EINVAL);
1422+
CKNULL(ip, -EINVAL);
1423+
CKNULL(ip_len, -EINVAL);
1424+
1425+
/* Check for IPv6 */
1426+
if (strstr(ip_name, ":")) {
1427+
tok = ":";
1428+
upper = 16;
1429+
base = 16;
1430+
}
1431+
1432+
CKRET(*ip_len < upper, -EOVERFLOW);
1433+
1434+
/* Unconstify is acceptable, as we only read the value with strtoul */
1435+
res = strtok_r((char *)ip_name, tok, &saveptr);
1436+
for (i = 0; i < upper; i++) {
1437+
CKNULL(res, -EINVAL);
1438+
val = strtoul(res, NULL, base);
1439+
CKRET(val > 255, -EINVAL);
1440+
ip[i] = (uint8_t)val;
1441+
res = strtok_r(NULL, tok, &saveptr);
1442+
}
1443+
1444+
*ip_len = i;
1445+
1446+
out:
1447+
return ret;
1448+
#endif
1449+
}

0 commit comments

Comments
 (0)