hx509: Add missing CSR extension request support

This is necessary in order to add proper support for CSRs in kx509,
where the KDC can examine all requested KUs/EKUs/SANs, check
authorization, and issue a certificate with all those extensions if
authorized.

This is the convention used by OpenSSL, of encoding all the KU, EKUs,
and SANs being requested as Extensions as they would appear in the
TBSCertificate, then putting those in as a single Attribute in the CSR's
Attributes list with attribute OID {id-pkcs-9, 14}.

 - expose all hx509_request_*() functions
 - finish support in hx509_request_parse*() for KU, EKU, and SAN CSR
   attributes
 - finish support in hx509_request_to_pkcs10() for encoding all
   requested KU, EKU, and SAN extensions as a CSR extReq (extension request)
 - add hx509_request_add_*() support for:
    - id-pkinit-san and ms-upn-pkinit-san
    - XMPP (Jabber) SAN
    - registeredID (useless but trivial)
 - add hxtool request-create options for all supported SANs
 - add hxtool request-create options for KeyUsage
 - add hxtool request-create options for ExtKeyUsage
 - add hxtool request-print support for all these things
 - fix bugs in existing id-pkinit-san handling

Possible future improvements

 - add HX509_TRACE env var and support (it would be nice to be able to
   observe why some certificate is rejected, or not matched in a query)
 - add testing that CSR creating and printing round-trip for all KUs,
   EKUs, and SANs
   (probably in tests/kdc/check-pkinit.in)
 - add testing that OpenSSL can print a CSR made by hxtool and
   vice-versa
 - hxtool ca: add KU sanity checking (via hx509_ca_sign() and/or friends)
   (don't allow encrypt for signing-only algs)
   (don't allow encrypt for RSA at all, or for RSA with small e exponents)
 - hxtool request-print: warn about all unknown attributes and
   extensions
 - hxtool ca: MAYBE add support for adding requested extensions from the
   --req=CSR
   ("Maybe" because CA operators should really verify and authorize all
    requested attributes, and should acknowledge that they have, and the
    simplest way to do this is to make them add all the corresponding
    CLI arguments to the hxtool ca command, but too, that is
    error-prone, thus it's not clear yet which approach is best.
    Perhaps interactively prompt for yes/no for each attribute.)
 - add additional SAN types:
    - iPAddress                 (useless?)
    - dNSSrv                    (useful!)
    - directoryName             (useless, but trivial)
    - uniformResourceIdentifier (useful)
 - it would be nice if the ASN.1 compiler could generate print
   functions..., and/or even better, to-JSON functions
 - it would be nice if we had a known-OID db, including the names of the
   types they refer to in certificate extensions, otherName SANs and CSR
   attributes, then we could generate a CSR and certificate printer for
   all known options even when they are not supported by the rest of
   Heimdal
    - and we could also get friendly names for OIDs, and we could
      resolve their arc names
    - longer term, we could also stand to add some ASN.1 information
      object system functionality, just enough to make
      lib/hx509/asn1_print awesome by being able to automatically decode
      all heim_any and OCTET STRING content (better than its current
      --inner option)
This commit is contained in:
Nicolas Williams
2019-07-16 14:51:59 -05:00
parent 6a7e7eace6
commit 8af2d79d35
13 changed files with 1554 additions and 226 deletions

View File

@@ -7,7 +7,6 @@ IMPORTS
Name, SubjectPublicKeyInfo, Attribute, AlgorithmIdentifier Name, SubjectPublicKeyInfo, Attribute, AlgorithmIdentifier
FROM rfc2459; FROM rfc2459;
CertificationRequestInfo ::= SEQUENCE { CertificationRequestInfo ::= SEQUENCE {
version INTEGER { pkcs10-v1(0) }, version INTEGER { pkcs10-v1(0) },
subject Name, subject Name,

View File

@@ -14,6 +14,7 @@ id-pkcs9-contentType OBJECT IDENTIFIER ::= {id-pkcs-9 3 }
id-pkcs9-messageDigest OBJECT IDENTIFIER ::= {id-pkcs-9 4 } id-pkcs9-messageDigest OBJECT IDENTIFIER ::= {id-pkcs-9 4 }
id-pkcs9-signingTime OBJECT IDENTIFIER ::= {id-pkcs-9 5 } id-pkcs9-signingTime OBJECT IDENTIFIER ::= {id-pkcs-9 5 }
id-pkcs9-countersignature OBJECT IDENTIFIER ::= {id-pkcs-9 6 } id-pkcs9-countersignature OBJECT IDENTIFIER ::= {id-pkcs-9 6 }
id-pkcs9-extReq OBJECT IDENTIFIER ::= {id-pkcs-9 14}
id-pkcs-9-at-friendlyName OBJECT IDENTIFIER ::= {id-pkcs-9 20} id-pkcs-9-at-friendlyName OBJECT IDENTIFIER ::= {id-pkcs-9 20}
id-pkcs-9-at-localKeyId OBJECT IDENTIFIER ::= {id-pkcs-9 21} id-pkcs-9-at-localKeyId OBJECT IDENTIFIER ::= {id-pkcs-9 21}

View File

@@ -185,9 +185,11 @@ DirectoryString ::= CHOICE {
bmpString BMPString bmpString BMPString
} }
AttributeValues ::= SET OF AttributeValue
Attribute ::= SEQUENCE { Attribute ::= SEQUENCE {
type AttributeType, type AttributeType,
value SET OF -- AttributeValue -- heim_any value AttributeValues
} }
AttributeTypeAndValue ::= SEQUENCE { AttributeTypeAndValue ::= SEQUENCE {

View File

@@ -604,6 +604,145 @@ hx509_ca_tbs_add_san_otherName(hx509_context context,
return add_GeneralNames(&tbs->san, &gn); return add_GeneralNames(&tbs->san, &gn);
} }
static
int
dequote_strndup(hx509_context context, const char *in, size_t len, char **out)
{
size_t i, k;
char *s;
*out = NULL;
if ((s = malloc(len + 1)) == NULL) {
hx509_set_error_string(context, 0, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
for (k = i = 0; i < len; i++) {
if (in[i] == '\\') {
switch (in[++i]) {
case 't': s[k++] = '\t'; break;
case 'b': s[k++] = '\b'; break;
case 'n': s[k++] = '\n'; break;
case '0':
for (i++; i < len; i++) {
if (in[i] == '\0')
break;
if (in[i++] == '\\' && in[i] == '0')
continue;
hx509_set_error_string(context, 0,
HX509_PARSING_NAME_FAILED,
"embedded NULs not supported in "
"PKINIT SANs");
free(s);
return HX509_PARSING_NAME_FAILED;
}
break;
case '\0':
hx509_set_error_string(context, 0,
HX509_PARSING_NAME_FAILED,
"trailing unquoted backslashes not "
"allowed in PKINIT SANs");
free(s);
return HX509_PARSING_NAME_FAILED;
default: s[k++] = in[i]; break;
}
} else {
s[k++] = in[i];
}
}
s[k] = '\0';
*out = s;
return 0;
}
int
_hx509_make_pkinit_san(hx509_context context,
const char *principal,
heim_octet_string *os)
{
KRB5PrincipalName p;
size_t size;
int ret;
os->data = NULL;
os->length = 0;
memset(&p, 0, sizeof(p));
/* Parse principal */
{
const char *str, *str_start;
size_t n, i;
/* Count number of components */
n = 1;
for (str = principal; *str != '\0' && *str != '@'; str++) {
if (*str == '\\') {
if (str[1] == '\0') {
ret = HX509_PARSING_NAME_FAILED;
hx509_set_error_string(context, 0, ret,
"trailing \\ in principal name");
goto out;
}
str++;
} else if(*str == '/') {
n++;
} else if(*str == '@') {
break;
}
}
if (*str != '@') {
/* Note that we allow the realm to be empty */
ret = HX509_PARSING_NAME_FAILED;
hx509_set_error_string(context, 0, ret, "Missing @ in principal");
goto out;
};
p.principalName.name_string.val =
calloc(n, sizeof(*p.principalName.name_string.val));
if (p.principalName.name_string.val == NULL) {
ret = ENOMEM;
hx509_set_error_string(context, 0, ret, "malloc: out of memory");
goto out;
}
p.principalName.name_string.len = n;
p.principalName.name_type = KRB5_NT_PRINCIPAL;
for (i = 0, str_start = str = principal; *str != '\0'; str++) {
if (*str=='\\') {
str++;
} else if(*str == '/') {
/* Note that we allow components to be empty */
ret = dequote_strndup(context, str_start, str - str_start,
&p.principalName.name_string.val[i++]);
if (ret)
goto out;
str_start = str + 1;
} else if(*str == '@') {
ret = dequote_strndup(context, str_start, str - str_start,
&p.principalName.name_string.val[i++]);
if (ret == 0)
ret = dequote_strndup(context, str + 1, strlen(str + 1), &p.realm);
if (ret)
goto out;
break;
}
}
}
ASN1_MALLOC_ENCODE(KRB5PrincipalName, os->data, os->length, &p, &size, ret);
if (ret) {
hx509_set_error_string(context, 0, ret, "Out of memory");
goto out;
}
if (size != os->length)
_hx509_abort("internal ASN.1 encoder error");
out:
free_KRB5PrincipalName(&p);
return ret;
}
/** /**
* Add Kerberos Subject Alternative Name to the to-be-signed * Add Kerberos Subject Alternative Name to the to-be-signed
* certificate object. The principal string is a UTF8 string. * certificate object. The principal string is a UTF8 string.
@@ -623,84 +762,13 @@ hx509_ca_tbs_add_san_pkinit(hx509_context context,
const char *principal) const char *principal)
{ {
heim_octet_string os; heim_octet_string os;
KRB5PrincipalName p;
size_t size;
int ret; int ret;
char *s = NULL;
memset(&p, 0, sizeof(p)); ret = _hx509_make_pkinit_san(context, principal, &os);
if (ret == 0)
/* parse principal */ ret = hx509_ca_tbs_add_san_otherName(context, tbs,
{ &asn1_oid_id_pkinit_san, &os);
const char *str;
char *q;
int n;
/* count number of component */
n = 1;
for(str = principal; *str != '\0' && *str != '@'; str++){
if(*str=='\\'){
if(str[1] == '\0' || str[1] == '@') {
ret = HX509_PARSING_NAME_FAILED;
hx509_set_error_string(context, 0, ret,
"trailing \\ in principal name");
goto out;
}
str++;
} else if(*str == '/')
n++;
}
p.principalName.name_string.val =
calloc(n, sizeof(*p.principalName.name_string.val));
if (p.principalName.name_string.val == NULL) {
ret = ENOMEM;
hx509_set_error_string(context, 0, ret, "malloc: out of memory");
goto out;
}
p.principalName.name_string.len = n;
p.principalName.name_type = KRB5_NT_PRINCIPAL;
q = s = strdup(principal);
if (q == NULL) {
ret = ENOMEM;
hx509_set_error_string(context, 0, ret, "malloc: out of memory");
goto out;
}
p.realm = strrchr(q, '@');
if (p.realm == NULL) {
ret = HX509_PARSING_NAME_FAILED;
hx509_set_error_string(context, 0, ret, "Missing @ in principal");
goto out;
};
*p.realm++ = '\0';
n = 0;
while (q) {
p.principalName.name_string.val[n++] = q;
q = strchr(q, '/');
if (q)
*q++ = '\0';
}
}
ASN1_MALLOC_ENCODE(KRB5PrincipalName, os.data, os.length, &p, &size, ret);
if (ret) {
hx509_set_error_string(context, 0, ret, "Out of memory");
goto out;
}
if (size != os.length)
_hx509_abort("internal ASN.1 encoder error");
ret = hx509_ca_tbs_add_san_otherName(context,
tbs,
&asn1_oid_id_pkinit_san,
&os);
free(os.data); free(os.data);
out:
if (p.principalName.name_string.val)
free (p.principalName.name_string.val);
if (s)
free(s);
return ret; return ret;
} }

View File

@@ -471,11 +471,36 @@ command = {
type = "strings" type = "strings"
help = "Email address in SubjectAltName" help = "Email address in SubjectAltName"
} }
option = {
long = "jid"
type = "strings"
help = "XMPP (Jabber) address in SubjectAltName"
}
option = { option = {
long = "dnsname" long = "dnsname"
type = "strings" type = "strings"
help = "Hostname or domainname in SubjectAltName" help = "Hostname or domainname in SubjectAltName"
} }
option = {
long = "kerberos"
type = "strings"
help = "Kerberos principal name as SubjectAltName"
}
option = {
long = "ms-kerberos"
type = "strings"
help = "Kerberos principal name as SubjectAltName (Microsoft variant)"
}
option = {
long = "registered"
type = "strings"
help = "Registered object ID as SubjectAltName"
}
option = {
long = "dn"
type = "strings"
help = "Directory name as SubjectAltName"
}
option = { option = {
long = "type" long = "type"
type = "string" type = "string"

View File

@@ -1370,29 +1370,65 @@ request_create(struct request_create_options *opt, int argc, char **argv)
char *s; char *s;
hx509_name_to_string(name, &s); hx509_name_to_string(name, &s);
printf("%s\n", s); printf("%s\n", s);
free(s);
} }
hx509_name_free(&name); hx509_name_free(&name);
} }
for (i = 0; i < opt->email_strings.num_strings; i++) { for (i = 0; i < opt->email_strings.num_strings; i++) {
ret = _hx509_request_add_email(context, req, ret = hx509_request_add_email(context, req,
opt->email_strings.strings[i]); opt->email_strings.strings[i]);
if (ret) if (ret)
hx509_err(context, 1, ret, "hx509_request_add_email"); hx509_err(context, 1, ret, "hx509_request_add_email");
} }
for (i = 0; i < opt->jid_strings.num_strings; i++) {
ret = hx509_request_add_xmpp_name(context, req,
opt->jid_strings.strings[i]);
if (ret)
hx509_err(context, 1, ret, "hx509_request_add_xmpp_name");
}
for (i = 0; i < opt->dnsname_strings.num_strings; i++) { for (i = 0; i < opt->dnsname_strings.num_strings; i++) {
ret = _hx509_request_add_dns_name(context, req, ret = hx509_request_add_dns_name(context, req,
opt->dnsname_strings.strings[i]); opt->dnsname_strings.strings[i]);
if (ret) if (ret)
hx509_err(context, 1, ret, "hx509_request_add_dns_name"); hx509_err(context, 1, ret, "hx509_request_add_dns_name");
} }
for (i = 0; i < opt->kerberos_strings.num_strings; i++) {
ret = hx509_request_add_pkinit(context, req,
opt->kerberos_strings.strings[i]);
if (ret)
hx509_err(context, 1, ret, "hx509_request_add_pkinit");
}
for (i = 0; i < opt->ms_kerberos_strings.num_strings; i++) {
ret = hx509_request_add_ms_upn_name(context, req,
opt->ms_kerberos_strings.strings[i]);
if (ret)
hx509_err(context, 1, ret, "hx509_request_add_ms_upn_name");
}
for (i = 0; i < opt->registered_strings.num_strings; i++) {
heim_oid oid;
ret = der_parse_heim_oid(opt->registered_strings.strings[i], NULL,
&oid);
if (ret)
hx509_err(context, 1, ret, "OID parse error");
ret = hx509_request_add_registered(context, req, &oid);
der_free_oid(&oid);
if (ret)
hx509_err(context, 1, ret, "hx509_request_add_registered");
}
for (i = 0; i < opt->eku_strings.num_strings; i++) { for (i = 0; i < opt->eku_strings.num_strings; i++) {
heim_oid oid; heim_oid oid;
parse_oid(opt->eku_strings.strings[i], NULL, &oid); parse_oid(opt->eku_strings.strings[i], NULL, &oid);
ret = _hx509_request_add_eku(context, req, &oid); ret = hx509_request_add_eku(context, req, &oid);
der_free_oid(&oid);
if (ret) if (ret)
hx509_err(context, 1, ret, "hx509_request_add_eku"); hx509_err(context, 1, ret, "hx509_request_add_eku");
} }
@@ -1409,12 +1445,12 @@ request_create(struct request_create_options *opt, int argc, char **argv)
if (ret) if (ret)
hx509_err(context, 1, ret, "hx509_request_set_SubjectPublicKeyInfo"); hx509_err(context, 1, ret, "hx509_request_set_SubjectPublicKeyInfo");
ret = _hx509_request_to_pkcs10(context, ret = hx509_request_to_pkcs10(context,
req, req,
signer, signer,
&request); &request);
if (ret) if (ret)
hx509_err(context, 1, ret, "_hx509_request_to_pkcs10"); hx509_err(context, 1, ret, "hx509_request_to_pkcs10");
hx509_private_key_free(&signer); hx509_private_key_free(&signer);
hx509_request_free(&req); hx509_request_free(&req);
@@ -1440,7 +1476,7 @@ request_print(struct request_print_options *opt, int argc, char **argv)
if (ret) if (ret)
hx509_err(context, 1, ret, "parse_request: %s", argv[i]); hx509_err(context, 1, ret, "parse_request: %s", argv[i]);
ret = _hx509_request_print(context, req, stdout); ret = hx509_request_print(context, req, stdout);
hx509_request_free(&req); hx509_request_free(&req);
if (ret) if (ret)
hx509_err(context, 1, ret, "Failed to print file %s", argv[i]); hx509_err(context, 1, ret, "Failed to print file %s", argv[i]);
@@ -1901,6 +1937,11 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
if (opt->req_string) { if (opt->req_string) {
hx509_request req; hx509_request req;
/*
* XXX Extract the CN and other attributes we want to preserve from the
* requested subjectName and then set them in the hx509_env for the
* template.
*/
ret = hx509_request_parse(context, opt->req_string, &req); ret = hx509_request_parse(context, opt->req_string, &req);
if (ret) if (ret)
hx509_err(context, 1, ret, "parse_request: %s", opt->req_string); hx509_err(context, 1, ret, "parse_request: %s", opt->req_string);
@@ -1910,6 +1951,7 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
ret = hx509_request_get_SubjectPublicKeyInfo(context, req, &spki); ret = hx509_request_get_SubjectPublicKeyInfo(context, req, &spki);
if (ret) if (ret)
hx509_err(context, 1, ret, "get spki"); hx509_err(context, 1, ret, "get spki");
/* XXX Add option to extract */
hx509_request_free(&req); hx509_request_free(&req);
} }

View File

@@ -12,31 +12,46 @@ EXPORTS
_hx509_generate_private_key_free _hx509_generate_private_key_free
_hx509_generate_private_key_init _hx509_generate_private_key_init
_hx509_generate_private_key_is_ca _hx509_generate_private_key_is_ca
_hx509_make_pkinit_san
_hx509_map_file_os _hx509_map_file_os
_hx509_name_from_Name _hx509_name_from_Name
hx509_private_key2SPKI hx509_private_key2SPKI
hx509_private_key_free hx509_private_key_free
_hx509_private_key_ref _hx509_private_key_ref
_hx509_request_add_dns_name hx509_request_add_GeneralName
_hx509_request_add_eku hx509_request_add_dns_name
_hx509_request_add_email hx509_request_add_eku
hx509_request_add_email
hx509_request_add_ms_upn_name
hx509_request_add_pkinit
hx509_request_add_registered
hx509_request_add_xmpp_name
_hx509_private_key_export _hx509_private_key_export
_hx509_private_key_exportable _hx509_private_key_exportable
_hx509_private_key_get_internal _hx509_private_key_get_internal
_hx509_private_key_oid _hx509_private_key_oid
_hx509_private_key_ref _hx509_private_key_ref
hx509_request_free hx509_request_free
hx509_request_get_dns_name_san
hx509_request_get_eku
hx509_request_get_email_san
hx509_request_get_exts
hx509_request_get_ku
hx509_request_get_ms_upn_san
hx509_request_get_name
hx509_request_get_pkinit_san
hx509_request_get_SubjectPublicKeyInfo hx509_request_get_SubjectPublicKeyInfo
hx509_request_get_xmpp_san
hx509_request_get_name hx509_request_get_name
hx509_request_init hx509_request_init
hx509_request_parse hx509_request_parse
hx509_request_parse_der hx509_request_parse_der
_hx509_request_print hx509_request_print
hx509_request_set_SubjectPublicKeyInfo hx509_request_set_SubjectPublicKeyInfo
; _hx509_request_set_email hx509_request_add_email
hx509_request_set_name hx509_request_set_name
_hx509_request_to_pkcs10 hx509_request_set_ku
_hx509_request_to_pkcs10 hx509_request_to_pkcs10
_hx509_unmap_file_os _hx509_unmap_file_os
_hx509_write_file _hx509_write_file
hx509_bitstring_print hx509_bitstring_print

View File

@@ -1001,6 +1001,102 @@ hx509_name_is_null_p(const hx509_name name)
name->der_name.u.rdnSequence.len == 0; name->der_name.u.rdnSequence.len == 0;
} }
/*
* This necessarily duplicates code from libkrb5, and has to unless we move
* common code here or to lib/roken for it. We do have slightly different
* needs (e.g., we want space quoted, and we want to indicate whether we saw
* trailing garbage, we have no need for flags, no special realm treatment,
* etc) than the corresponding code in libkrb5, so for now we duplicate this
* code.
*
* The relevant RFCs here are RFC1964 for the string representation of Kerberos
* principal names, and RFC4556 for the KRB5PrincipalName ASN.1 type (Kerberos
* lacks such a type because on the wire the name and realm are sent
* separately as a form of cheap compression).
*
* Note that we cannot handle embedded NULs because of Heimdal's representation
* of ASN.1 strings as C strings.
*/
struct rk_strpool *
_hx509_unparse_kerberos_name(struct rk_strpool *strpool, heim_any *value)
{
static const char comp_quotable_chars[] = " \n\t\b\\/@";
static const char realm_quotable_chars[] = " \n\t\b\\@";
KRB5PrincipalName kn;
const char *s;
size_t i, k, len, plen;
int extra_bits;
int need_slash = 0;
int ret;
ret = decode_KRB5PrincipalName(value->data, value->length, &kn, &len);
if (ret)
return rk_strpoolprintf(strpool, "<error-decoding-PrincipalName");
extra_bits = (value->length != len);
for (i = 0; i < kn.principalName.name_string.len; i++) {
s = kn.principalName.name_string.val[i];
len = strlen(s);
if (need_slash)
strpool = rk_strpoolprintf(strpool, "/");
need_slash = 1;
for (k = 0; k < len; s += plen, k += plen) {
char c;
plen = strcspn(s, comp_quotable_chars);
if (plen)
strpool = rk_strpoolprintf(strpool, "%.*s", (int)plen, s);
if (k + plen >= len)
continue;
switch ((c = s[plen++])) {
case '\n': strpool = rk_strpoolprintf(strpool, "\\n"); break;
case '\t': strpool = rk_strpoolprintf(strpool, "\\t"); break;
case '\b': strpool = rk_strpoolprintf(strpool, "\\b"); break;
/* default -> '@', ' ', '\\', or '/' */
default: strpool = rk_strpoolprintf(strpool, "\\%c", c); break;
}
}
}
strpool = rk_strpoolprintf(strpool, "@");
s = kn.realm;
len = strlen(kn.realm);
for (k = 0; k < len; s += plen, k += plen) {
char c;
plen = strcspn(s, realm_quotable_chars);
if (plen)
strpool = rk_strpoolprintf(strpool, "%.*s", (int)plen, s);
if (k + plen >= len)
continue;
switch ((c = s[plen++])) {
case '\n': strpool = rk_strpoolprintf(strpool, "\\n"); break;
case '\t': strpool = rk_strpoolprintf(strpool, "\\t"); break;
case '\b': strpool = rk_strpoolprintf(strpool, "\\b"); break;
/* default -> '@', ' ', or '\\' */
default: strpool = rk_strpoolprintf(strpool, "\\%c", c); break;
}
}
if (extra_bits)
strpool = rk_strpoolprintf(strpool, " <garbage>");
free_KRB5PrincipalName(&kn);
return strpool;
}
struct rk_strpool *
hx509_unparse_utf8_string_name(struct rk_strpool *strpool, heim_any *value)
{
PKIXXmppAddr us;
size_t size;
if (decode_PKIXXmppAddr(value->data, value->length, &us, &size))
return rk_strpoolprintf(strpool, "<decode-error>");
strpool = rk_strpoolprintf(strpool, "%s", us);
free_PKIXXmppAddr(&us);
return strpool;
}
/** /**
* Unparse the hx509 name in name into a string. * Unparse the hx509 name in name into a string.
* *
@@ -1025,17 +1121,34 @@ hx509_general_name_unparse(GeneralName *name, char **str)
hx509_oid_sprint(&name->u.otherName.type_id, &oid); hx509_oid_sprint(&name->u.otherName.type_id, &oid);
if (oid == NULL) if (oid == NULL)
return ENOMEM; return ENOMEM;
strpool = rk_strpoolprintf(strpool, "otherName: %s", oid); strpool = rk_strpoolprintf(strpool, "otherName: %s ", oid);
if (der_heim_oid_cmp(&name->u.otherName.type_id,
&asn1_oid_id_pkinit_san) == 0) {
strpool = _hx509_unparse_kerberos_name(strpool,
&name->u.otherName.value);
} else if (der_heim_oid_cmp(&name->u.otherName.type_id,
&asn1_oid_id_pkix_on_xmppAddr) == 0) {
strpool = rk_strpoolprintf(strpool, "xmppAddr ");
strpool = hx509_unparse_utf8_string_name(strpool,
&name->u.otherName.value);
} else if (der_heim_oid_cmp(&name->u.otherName.type_id,
&asn1_oid_id_pkinit_ms_san) == 0) {
strpool = rk_strpoolprintf(strpool, "pkinitMsSan ");
strpool = hx509_unparse_utf8_string_name(strpool,
&name->u.otherName.value);
} else {
strpool = rk_strpoolprintf(strpool, "<unknown-other-name-type");
}
free(oid); free(oid);
break; break;
} }
case choice_GeneralName_rfc822Name: case choice_GeneralName_rfc822Name:
strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s\n", strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s",
(int)name->u.rfc822Name.length, (int)name->u.rfc822Name.length,
(char *)name->u.rfc822Name.data); (char *)name->u.rfc822Name.data);
break; break;
case choice_GeneralName_dNSName: case choice_GeneralName_dNSName:
strpool = rk_strpoolprintf(strpool, "dNSName: %.*s\n", strpool = rk_strpoolprintf(strpool, "dNSName: %.*s",
(int)name->u.dNSName.length, (int)name->u.dNSName.length,
(char *)name->u.dNSName.data); (char *)name->u.dNSName.data);
break; break;
@@ -1095,10 +1208,8 @@ hx509_general_name_unparse(GeneralName *name, char **str)
default: default:
return EINVAL; return EINVAL;
} }
if (strpool == NULL) if (strpool == NULL ||
(*str = rk_strpoolcollect(strpool)) == NULL)
return ENOMEM; return ENOMEM;
*str = rk_strpoolcollect(strpool);
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -349,6 +349,74 @@ test_compare(hx509_context context)
return 0; return 0;
} }
static int
test_pkinit_san(hx509_context context, const char *p, const char *realm, ...)
{
KRB5PrincipalName kn;
GeneralName gn;
va_list ap;
size_t i, sz;
char *round_trip;
int ret;
memset(&kn, 0, sizeof(kn));
memset(&gn, 0, sizeof(gn));
ret = _hx509_make_pkinit_san(context, p, &gn.u.otherName.value);
if (ret == 0)
ret = decode_KRB5PrincipalName(gn.u.otherName.value.data,
gn.u.otherName.value.length, &kn, &sz);
if (ret)
return ret;
if (strcmp(realm, kn.realm))
return ret;
va_start(ap, realm);
for (i = 0; i < kn.principalName.name_string.len; i++) {
const char *s = va_arg(ap, const char *);
if (s == NULL || strcmp(kn.principalName.name_string.val[i], s))
return 1;
}
if (va_arg(ap, const char *) != NULL)
return 1;
va_end(ap);
gn.element = choice_GeneralName_otherName;
gn.u.otherName.type_id.length = 0;
gn.u.otherName.type_id.components = 0;
ret = der_copy_oid(&asn1_oid_id_pkinit_san, &gn.u.otherName.type_id);
if (ret == 0)
ret = hx509_general_name_unparse(&gn, &round_trip);
if (ret)
return 1;
if (strncmp(round_trip, "otherName: 1.3.6.1.5.2.2 ",
sizeof("otherName: 1.3.6.1.5.2.2 ") - 1))
return 1;
if (ret || strcmp(round_trip + sizeof("otherName: 1.3.6.1.5.2.2 ") - 1, p))
return 1;
free_KRB5PrincipalName(&kn);
free_GeneralName(&gn);
free(round_trip);
return 0;
}
static int
test_pkinit_san_fail(hx509_context context, const char *p)
{
heim_octet_string os;
KRB5PrincipalName kn;
int ret;
memset(&kn, 0, sizeof(kn));
ret = _hx509_make_pkinit_san(context, p, &os);
if (ret == 0) {
free(os.data);
return 1;
}
return 0;
}
int int
main(int argc, char **argv) main(int argc, char **argv)
@@ -376,7 +444,25 @@ main(int argc, char **argv)
ret += test_compare(context); ret += test_compare(context);
ret += test_pkinit_san(context, "foo@BAR.H5L.SE",
"BAR.H5L.SE", "foo", NULL);
ret += test_pkinit_san(context, "foo\\ bar@BAR.H5L.SE",
"BAR.H5L.SE", "foo bar", NULL);
ret += test_pkinit_san(context, "foo\\/bar@BAR.H5L.SE",
"BAR.H5L.SE", "foo/bar", NULL);
ret += test_pkinit_san(context, "foo/bar@BAR.H5L.SE",
"BAR.H5L.SE", "foo", "bar", NULL);
ret += test_pkinit_san(context, "foo\\tbar@BAR.H5L.SE",
"BAR.H5L.SE", "foo\tbar", NULL);
ret += test_pkinit_san(context, "foo\\nbar@BAR.H5L.SE",
"BAR.H5L.SE", "foo\nbar", NULL);
ret += test_pkinit_san(context, "foo@\\ BAR.H5L.SE",
" BAR.H5L.SE", "foo", NULL);
ret += test_pkinit_san(context, "foo@\\nBAR.H5L.SE",
"\nBAR.H5L.SE", "foo", NULL);
ret += test_pkinit_san_fail(context, "foo\\0bar@BAR.H5L.SE");
hx509_context_free(&context); hx509_context_free(&context);
return ret; return !!ret;
} }

View File

@@ -50,14 +50,84 @@ fi
${hxtool} request-create \ ${hxtool} request-create \
--subject="CN=Love,DC=it,DC=su,DC=se" \ --subject="CN=Love,DC=it,DC=su,DC=se" \
--key=FILE:$srcdir/data/key.der \ --key="FILE:$srcdir/data/key.der" \
request.out || exit 1 "${objdir}/request.out" || exit 1
${hxtool} request-print \ ${hxtool} request-print \
PKCS10:request.out > /dev/null || exit 1 PKCS10:request.out > /dev/null || exit 1
${hxtool} request-create \ ${hxtool} request-create \
--subject="CN=Love,DC=it,DC=su,DC=se" \ --subject="CN=Love,DC=it,DC=su,DC=se" \
--dnsname=nutcracker.it.su.se \ --eku=1.2.3.4.5.6.7 --eku=1.2.3.4.5.6.8 \
--key=FILE:$srcdir/data/key.der \ --registered=1.2.3.4.5.6.9 --eku=1.2.3.4.5.6.10 \
request.out || exit 1 --dnsname=nutcracker.test.h5l.se \
--dnsname=foo.nutcracker.test.h5l.se \
--kerberos=HTTP/foo.nutcracker.it.su.se@TEST.H5L.SE \
--kerberos=host/foo.nutcracker.it.su.se@TEST.H5L.SE \
--email=foo@test.h5l.se \
--key="FILE:$srcdir/data/key.der" \
"${objdir}/request.out" || exit 1
cat > "$objdir/expected" <<EOF
request print
PKCS#10 CertificationRequest:
name: CN=Love,DC=it,DC=su,DC=se
eku: {1.2.3.4.5.6.7}, {1.2.3.4.5.6.8}, {1.2.3.4.5.6.10}
san: rfc822Name: foo@test.h5l.se
san: dNSName: nutcracker.test.h5l.se
san: dNSName: foo.nutcracker.test.h5l.se
san: otherName: 1.3.6.1.5.2.2 HTTP/foo.nutcracker.it.su.se@TEST.H5L.SE
san: otherName: 1.3.6.1.5.2.2 host/foo.nutcracker.it.su.se@TEST.H5L.SE
san: registeredID: 1.2.3.4.5.6.9
EOF
# Check that we got what we wanted:
${hxtool} request-print \
PKCS10:request.out > "${objdir}/actual" || exit 1
diff "$objdir/expected" "${objdir}/actual" || exit 1
if openssl version > /dev/null &&
openssl req -inform DER -in "${objdir}/request.out" -text |
grep 'Version: 0'; then
v=0
k=
else
v=1
k="RSA "
fi
# Check that OpenSSL can parse our request:
cat > "$objdir/expected" <<EOF
Certificate Request:
Data:
Version: $v (0x0)
Subject: DC = se, DC = su, DC = it, CN = Love
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
${k}Public-Key: (1024 bit)
Modulus:
00:c2:aa:a2:42:b7:5b:99:a3:fd:ba:f0:9b:75:db:
ef:3c:9b:8c:cf:63:5f:46:d8:95:be:09:4a:a7:76:
79:77:61:30:ef:0b:98:d2:47:ea:9c:09:b9:b9:b7:
15:ac:4b:9c:2d:3f:f0:d9:99:9d:4d:5a:68:67:24:
58:5e:65:60:13:9f:4d:dc:2f:03:1d:cd:e9:b6:33:
c2:5c:c6:de:c9:93:6c:ec:8d:9a:67:0e:dd:31:20:
ac:91:39:7a:c1:8f:39:65:ff:b3:1f:cf:7a:aa:79:
8b:ed:eb:ad:a0:be:01:10:4c:5a:a7:47:1d:c6:ee:
79:39:5c:c7:11:6c:b9:e7:2b
Exponent: 65537 (0x10001)
Attributes:
Requested Extensions:
X509v3 Extended Key Usage: critical
1.2.3.4.5.6.7, 1.2.3.4.5.6.8, 1.2.3.4.5.6.10
X509v3 Subject Alternative Name:
email:foo@test.h5l.se, DNS:nutcracker.test.h5l.se, DNS:foo.nutcracker.test.h5l.se, othername:<unsupported>, othername:<unsupported>, Registered ID:1.2.3.4.5.6.9
Signature Algorithm: sha256WithRSAEncryption
EOF
if openssl version > /dev/null; then
openssl req -inform DER -in "${objdir}/request.out" -text | head -25 > "${objdir}/actual"
diff -w "${objdir}/expected" "${objdir}/actual" || exit 1
fi

View File

@@ -14,6 +14,7 @@ HEIMDAL_X509_1.2 {
_hx509_generate_private_key_free; _hx509_generate_private_key_free;
_hx509_generate_private_key_init; _hx509_generate_private_key_init;
_hx509_generate_private_key_is_ca; _hx509_generate_private_key_is_ca;
_hx509_make_pkinit_san;
_hx509_map_file_os; _hx509_map_file_os;
_hx509_name_from_Name; _hx509_name_from_Name;
_hx509_private_key_export; _hx509_private_key_export;
@@ -21,12 +22,16 @@ HEIMDAL_X509_1.2 {
_hx509_private_key_get_internal; _hx509_private_key_get_internal;
_hx509_private_key_oid; _hx509_private_key_oid;
_hx509_private_key_ref; _hx509_private_key_ref;
_hx509_request_add_dns_name; hx509_request_add_GeneralName;
_hx509_request_add_eku; hx509_request_add_dns_name;
_hx509_request_add_email; hx509_request_add_eku;
_hx509_request_print; hx509_request_add_email;
_hx509_request_set_email; hx509_request_add_ms_upn_name;
_hx509_request_to_pkcs10; hx509_request_add_pkinit;
hx509_request_add_registered;
hx509_request_add_xmpp_name;
hx509_request_print;
hx509_request_to_pkcs10;
_hx509_unmap_file_os; _hx509_unmap_file_os;
_hx509_write_file; _hx509_write_file;
hx509_bitstring_print; hx509_bitstring_print;
@@ -206,12 +211,21 @@ HEIMDAL_X509_1.2 {
hx509_query_match_option; hx509_query_match_option;
hx509_query_statistic_file; hx509_query_statistic_file;
hx509_query_unparse_stats; hx509_query_unparse_stats;
hx509_request_get_dns_name_san;
hx509_request_get_eku;
hx509_request_get_email_san;
hx509_request_get_exts;
hx509_request_get_ku;
hx509_request_get_ms_upn_san;
hx509_request_get_name; hx509_request_get_name;
hx509_request_get_pkinit_san;
hx509_request_get_SubjectPublicKeyInfo; hx509_request_get_SubjectPublicKeyInfo;
hx509_request_get_xmpp_san;
hx509_request_free; hx509_request_free;
hx509_request_init; hx509_request_init;
hx509_request_parse; hx509_request_parse;
hx509_request_parse_der; hx509_request_parse_der;
hx509_request_set_ku;
hx509_request_set_name; hx509_request_set_name;
hx509_request_set_SubjectPublicKeyInfo; hx509_request_set_SubjectPublicKeyInfo;
hx509_revoke_add_crl; hx509_revoke_add_crl;

View File

@@ -210,7 +210,7 @@ unparse_something (int num, const struct units *units, char *s, size_t len,
tmp = (*print) (s, len, divisor, u->name, num); tmp = (*print) (s, len, divisor, u->name, num);
if (tmp < 0) if (tmp < 0)
return tmp; return tmp;
if (tmp > (int) len) { if ((size_t)tmp > len) {
len = 0; len = 0;
s = NULL; s = NULL;
} else { } else {