2 * Copyright (c) 2004 - 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * @page page_name PKIX/X.509 Names
41 * There are several names in PKIX/X.509, GeneralName and Name.
43 * A Name consists of an ordered list of Relative Distinguished Names
44 * (RDN). Each RDN consists of an unordered list of typed strings. The
45 * types are defined by OID and have long and short description. For
46 * example id-at-commonName (2.5.4.3) have the long name CommonName
47 * and short name CN. The string itself can be of several encoding,
48 * UTF8, UTF16, Teltex string, etc. The type limit what encoding
51 * GeneralName is a broader nametype that can contains al kind of
52 * stuff like Name, IP addresses, partial Name, etc.
54 * Name is mapped into a hx509_name object.
56 * Parse and string name into a hx509_name object with hx509_parse_name(),
57 * make it back into string representation with hx509_name_to_string().
59 * Name string are defined rfc2253, rfc1779 and X.501.
61 * See the library functions here: @ref hx509_name
67 int type_choice
; /* Preference for DirectoryString choice; 0 -> no pref */
68 wind_profile_flags flags
;
70 * RFC52380 imposes maximum lengths for some strings in Names. These are
71 * ASN.1 size limits. We should implement these in our copy of the PKIX
72 * ASN.1 module. For now we treat them as maximum byte counts rather than
73 * maximum character counts, and we encode and enforce them here.
77 * Some of these attributes aren't of type DirectoryString, so our
78 * type_choice isn't really correct. We're not really set up for
79 * attributes whose types aren't DirectoryString or one of its choice arms'
80 * type, much less are we set up for non-string attribute value types.
84 { "C", &asn1_oid_id_at_countryName
,
85 choice_DirectoryString_printableString
, 0, 2 },
86 { "CN", &asn1_oid_id_at_commonName
, 0, 0, ub_common_name
},
87 { "DC", &asn1_oid_id_domainComponent
, choice_DirectoryString_ia5String
,
88 0, 63 }, /* DNS label */
89 { "L", &asn1_oid_id_at_localityName
, 0, 0, ub_locality_name
},
90 { "O", &asn1_oid_id_at_organizationName
, 0, 0, ub_organization_name
},
91 { "OU", &asn1_oid_id_at_organizationalUnitName
, 0, 0,
92 ub_organizational_unit_name
},
93 { "S", &asn1_oid_id_at_stateOrProvinceName
, 0, 0, ub_state_name
},
94 { "STREET", &asn1_oid_id_at_streetAddress
, 0, 0, 0 }, /* ENOTSUP */
95 { "UID", &asn1_oid_id_Userid
, 0, 0, ub_numeric_user_id_length
},
96 { "emailAddress", &asn1_oid_id_pkcs9_emailAddress
,
97 choice_DirectoryString_ia5String
, 0, ub_emailaddress_length
},
98 /* This is for DevID certificates and maybe others */
99 { "serialNumber", &asn1_oid_id_at_serialNumber
, 0, 0, ub_serial_number
},
100 /* These are for TPM 2.0 Endorsement Key Certificates (EKCerts) */
101 { "TPMManufacturer", &asn1_oid_tcg_at_tpmManufacturer
, 0, 0,
102 ub_emailaddress_length
},
103 { "TPMModel", &asn1_oid_tcg_at_tpmModel
, 0, 0, ub_emailaddress_length
},
104 { "TPMVersion", &asn1_oid_tcg_at_tpmVersion
, 0, 0, ub_emailaddress_length
},
108 quote_string(const char *f
, size_t len
, int flags
, size_t *rlen
)
111 const unsigned char *from
= (const unsigned char *)f
;
119 for (i
= 0, j
= 0; i
< len
; i
++) {
120 unsigned char map
= char_map
[from
[i
]] & flags
;
121 if (i
== 0 && (map
& Q_RFC2253_QUOTE_FIRST
)) {
124 } else if ((i
+ 1) == len
&& (map
& Q_RFC2253_QUOTE_LAST
)) {
128 } else if (map
& Q_RFC2253_QUOTE
) {
131 } else if (map
& Q_RFC2253_HEX
) {
132 int l
= snprintf((char *)&to
[j
], tolen
- j
- 1,
133 "#%02x", (unsigned char)from
[i
]);
147 append_string(char **str
, size_t *total_len
, const char *ss
,
148 size_t len
, int quote
)
153 qs
= quote_string(ss
, len
, Q_RFC2253
, &len
);
157 s
= realloc(*str
, len
+ *total_len
+ 1);
159 _hx509_abort("allocation failure"); /* XXX */
160 memcpy(s
+ *total_len
, qs
, len
);
163 s
[*total_len
+ len
] = '\0';
170 oidtostring(const heim_oid
*type
, int *type_choice
)
176 *type_choice
= choice_DirectoryString_utf8String
;
178 for (i
= 0; i
< sizeof(no
)/sizeof(no
[0]); i
++) {
179 if (der_heim_oid_cmp(no
[i
].o
, type
) == 0) {
180 if (type_choice
&& no
[i
].type_choice
)
181 *type_choice
= no
[i
].type_choice
;
182 return strdup(no
[i
].n
);
185 if (der_print_heim_oid(type
, '.', &s
) != 0)
191 oidtomaxlen(const heim_oid
*type
)
195 for (i
= 0; i
< sizeof(no
)/sizeof(no
[0]); i
++) {
196 if (der_heim_oid_cmp(no
[i
].o
, type
) == 0)
197 return no
[i
].max_bytes
;
203 stringtooid(const char *name
, size_t len
, heim_oid
*oid
)
209 memset(oid
, 0, sizeof(*oid
));
211 for (i
= 0; i
< sizeof(no
)/sizeof(no
[0]); i
++) {
212 if (strncasecmp(no
[i
].n
, name
, len
) == 0)
213 return der_copy_oid(no
[i
].o
, oid
);
218 memcpy(s
, name
, len
);
220 ret
= der_parse_heim_oid(s
, ".", oid
);
226 * Convert the hx509 name object into a printable string.
227 * The resulting string should be freed with free().
229 * @param name name to print
230 * @param str the string to return
232 * @return An hx509 error code, see hx509_get_error_string().
234 * @ingroup hx509_name
237 HX509_LIB_FUNCTION
int HX509_LIB_CALL
238 hx509_name_to_string(const hx509_name name
, char **str
)
240 return _hx509_Name_to_string(&name
->der_name
, str
);
243 HX509_LIB_FUNCTION
int HX509_LIB_CALL
244 _hx509_Name_to_string(const Name
*n
, char **str
)
246 size_t total_len
= 0;
254 for (m
= n
->u
.rdnSequence
.len
; m
> 0; m
--) {
258 for (j
= 0; j
< n
->u
.rdnSequence
.val
[i
].len
; j
++) {
259 DirectoryString
*ds
= &n
->u
.rdnSequence
.val
[i
].val
[j
].value
;
263 oidname
= oidtostring(&n
->u
.rdnSequence
.val
[i
].val
[j
].type
, NULL
);
265 switch(ds
->element
) {
266 case choice_DirectoryString_ia5String
:
267 ss
= ds
->u
.ia5String
.data
;
268 len
= ds
->u
.ia5String
.length
;
270 case choice_DirectoryString_printableString
:
271 ss
= ds
->u
.printableString
.data
;
272 len
= ds
->u
.printableString
.length
;
274 case choice_DirectoryString_utf8String
:
275 ss
= ds
->u
.utf8String
;
278 case choice_DirectoryString_bmpString
: {
279 const uint16_t *bmp
= ds
->u
.bmpString
.data
;
280 size_t bmplen
= ds
->u
.bmpString
.length
;
283 ret
= wind_ucs2utf8_length(bmp
, bmplen
, &k
);
293 _hx509_abort("allocation failure"); /* XXX */
294 ret
= wind_ucs2utf8(bmp
, bmplen
, ss
, NULL
);
306 case choice_DirectoryString_teletexString
:
307 ss
= ds
->u
.teletexString
;
310 case choice_DirectoryString_universalString
: {
311 const uint32_t *uni
= ds
->u
.universalString
.data
;
312 size_t unilen
= ds
->u
.universalString
.length
;
315 ret
= wind_ucs4utf8_length(uni
, unilen
, &k
);
325 _hx509_abort("allocation failure"); /* XXX */
326 ret
= wind_ucs4utf8(uni
, unilen
, ss
, NULL
);
339 _hx509_abort("unknown directory type: %d", ds
->element
);
342 append_string(str
, &total_len
, oidname
, strlen(oidname
), 0);
344 append_string(str
, &total_len
, "=", 1, 0);
345 append_string(str
, &total_len
, ss
, len
, 1);
346 if (ds
->element
== choice_DirectoryString_bmpString
||
347 ds
->element
== choice_DirectoryString_universalString
)
351 if (j
+ 1 < n
->u
.rdnSequence
.val
[i
].len
)
352 append_string(str
, &total_len
, "+", 1, 0);
356 append_string(str
, &total_len
, ",", 1, 0);
361 #define COPYCHARARRAY(_ds,_el,_l,_n) \
362 (_l) = strlen(_ds->u._el); \
363 (_n) = malloc((_l) * sizeof((_n)[0])); \
366 for (i = 0; i < (_l); i++) \
367 (_n)[i] = _ds->u._el[i]
370 #define COPYVALARRAY(_ds,_el,_l,_n) \
371 (_l) = _ds->u._el.length; \
372 (_n) = malloc((_l) * sizeof((_n)[0])); \
375 for (i = 0; i < (_l); i++) \
376 (_n)[i] = _ds->u._el.data[i]
378 #define COPYVOIDARRAY(_ds,_el,_l,_n) \
379 (_l) = _ds->u._el.length; \
380 (_n) = malloc((_l) * sizeof((_n)[0])); \
383 for (i = 0; i < (_l); i++) \
384 (_n)[i] = ((unsigned char *)_ds->u._el.data)[i]
389 dsstringprep(const DirectoryString
*ds
, uint32_t **rname
, size_t *rlen
)
391 wind_profile_flags flags
;
399 switch(ds
->element
) {
400 case choice_DirectoryString_ia5String
:
401 flags
= WIND_PROFILE_LDAP
;
402 COPYVOIDARRAY(ds
, ia5String
, len
, name
);
404 case choice_DirectoryString_printableString
:
405 flags
= WIND_PROFILE_LDAP
;
406 flags
|= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE
;
407 COPYVOIDARRAY(ds
, printableString
, len
, name
);
409 case choice_DirectoryString_teletexString
:
410 flags
= WIND_PROFILE_LDAP_CASE
;
411 COPYCHARARRAY(ds
, teletexString
, len
, name
);
413 case choice_DirectoryString_bmpString
:
414 flags
= WIND_PROFILE_LDAP
;
415 COPYVALARRAY(ds
, bmpString
, len
, name
);
417 case choice_DirectoryString_universalString
:
418 flags
= WIND_PROFILE_LDAP
;
419 COPYVALARRAY(ds
, universalString
, len
, name
);
421 case choice_DirectoryString_utf8String
:
422 flags
= WIND_PROFILE_LDAP
;
423 ret
= wind_utf8ucs4_length(ds
->u
.utf8String
, &len
);
426 name
= malloc(len
* sizeof(name
[0]));
429 ret
= wind_utf8ucs4(ds
->u
.utf8String
, name
, &len
);
436 _hx509_abort("unknown directory type: %d", ds
->element
);
440 /* try a couple of times to get the length right, XXX gross */
441 for (i
= 0; i
< 4; i
++) {
443 if ((*rname
= malloc(*rlen
* sizeof((*rname
)[0]))) == NULL
) {
448 ret
= wind_stringprep(name
, len
, *rname
, rlen
, flags
);
449 if (ret
== WIND_ERR_OVERRUN
) {
468 HX509_LIB_FUNCTION
int HX509_LIB_CALL
469 _hx509_name_ds_cmp(const DirectoryString
*ds1
,
470 const DirectoryString
*ds2
,
473 uint32_t *ds1lp
, *ds2lp
;
474 size_t ds1len
, ds2len
, i
;
477 ret
= dsstringprep(ds1
, &ds1lp
, &ds1len
);
480 ret
= dsstringprep(ds2
, &ds2lp
, &ds2len
);
486 if (ds1len
!= ds2len
)
487 *diff
= ds1len
- ds2len
;
489 for (i
= 0; i
< ds1len
; i
++) {
490 *diff
= ds1lp
[i
] - ds2lp
[i
];
501 HX509_LIB_FUNCTION
int HX509_LIB_CALL
502 _hx509_name_cmp(const Name
*n1
, const Name
*n2
, int *c
)
507 *c
= n1
->u
.rdnSequence
.len
- n2
->u
.rdnSequence
.len
;
511 for (i
= 0 ; i
< n1
->u
.rdnSequence
.len
; i
++) {
512 *c
= n1
->u
.rdnSequence
.val
[i
].len
- n2
->u
.rdnSequence
.val
[i
].len
;
516 for (j
= 0; j
< n1
->u
.rdnSequence
.val
[i
].len
; j
++) {
517 *c
= der_heim_oid_cmp(&n1
->u
.rdnSequence
.val
[i
].val
[j
].type
,
518 &n1
->u
.rdnSequence
.val
[i
].val
[j
].type
);
522 ret
= _hx509_name_ds_cmp(&n1
->u
.rdnSequence
.val
[i
].val
[j
].value
,
523 &n2
->u
.rdnSequence
.val
[i
].val
[j
].value
,
536 * Compare to hx509 name object, useful for sorting.
538 * @param n1 a hx509 name object.
539 * @param n2 a hx509 name object.
541 * @return 0 the objects are the same, returns > 0 is n2 is "larger"
542 * then n2, < 0 if n1 is "smaller" then n2.
544 * @ingroup hx509_name
547 HX509_LIB_FUNCTION
int HX509_LIB_CALL
548 hx509_name_cmp(hx509_name n1
, hx509_name n2
)
551 ret
= _hx509_name_cmp(&n1
->der_name
, &n2
->der_name
, &diff
);
558 HX509_LIB_FUNCTION
int HX509_LIB_CALL
559 _hx509_name_from_Name(const Name
*n
, hx509_name
*name
)
562 *name
= calloc(1, sizeof(**name
));
565 ret
= copy_Name(n
, &(*name
)->der_name
);
573 HX509_LIB_FUNCTION
int HX509_LIB_CALL
574 _hx509_name_modify(hx509_context context
,
580 RelativeDistinguishedName rdn
;
581 size_t max_len
= oidtomaxlen(oid
);
582 int type_choice
, ret
;
583 const char *a
= oidtostring(oid
, &type_choice
);
587 * Check string length upper bounds.
589 * Because we don't have these bounds in our copy of the PKIX ASN.1 module,
590 * and because we might like to catch these early anyways, we enforce them
593 if (max_len
&& strlen(str
) > max_len
) {
594 ret
= HX509_PARSING_NAME_FAILED
;
595 hx509_set_error_string(context
, 0, ret
, "RDN attribute %s value too "
596 "long (max %llu): %s", a
? a
: "<unknown>",
601 memset(&rdn
, 0, sizeof(rdn
));
602 if ((rdn
.val
= malloc(sizeof(rdn
.val
[0]))) == NULL
) {
603 hx509_set_error_string(context
, 0, ENOMEM
, "Out of memory");
609 * How best to pick a type for this attribute value?
613 * 1) the API deals only in UTF-8, let the callers convert to/from UTF-8
614 * and whatever the current locale wants
616 * 2) use the best type for the codeset of the current locale.
620 * However, for some cases we really should prefer other types when the
621 * input string is all printable ASCII.
623 rdn
.val
[0].value
.element
= type_choice
;
624 if ((s
= strdup(str
)) == NULL
||
625 (ret
= der_copy_oid(oid
, &rdn
.val
[0].type
))) {
628 return hx509_enomem(context
);
630 switch (rdn
.val
[0].value
.element
) {
632 case choice_DirectoryString_utf8String
:
633 rdn
.val
[0].value
.u
.utf8String
= s
;
635 case choice_DirectoryString_teletexString
:
636 rdn
.val
[0].value
.u
.teletexString
= s
;
639 /* Length and pointer */
640 case choice_DirectoryString_ia5String
:
641 rdn
.val
[0].value
.u
.ia5String
.data
= s
;
642 rdn
.val
[0].value
.u
.ia5String
.length
= strlen(s
);
644 case choice_DirectoryString_printableString
:
645 rdn
.val
[0].value
.u
.printableString
.data
= s
;
646 rdn
.val
[0].value
.u
.printableString
.length
= strlen(s
);
648 case choice_DirectoryString_universalString
:
651 hx509_set_error_string(context
, 0, ENOTSUP
, "UniversalString not supported");
653 case choice_DirectoryString_bmpString
:
656 hx509_set_error_string(context
, 0, ENOTSUP
, "BMPString not supported");
661 hx509_set_error_string(context
, 0, ENOTSUP
,
662 "Internal error; unknown DirectoryString choice");
666 /* Append RDN. If the caller wanted to prepend instead, we'll rotate. */
667 ret
= add_RDNSequence(&name
->u
.rdnSequence
, &rdn
);
668 free_RelativeDistinguishedName(&rdn
);
670 if (ret
|| append
|| name
->u
.rdnSequence
.len
< 2)
674 rdn
= name
->u
.rdnSequence
.val
[name
->u
.rdnSequence
.len
- 1];
675 memmove(&name
->u
.rdnSequence
.val
[1],
676 &name
->u
.rdnSequence
.val
[0],
677 (name
->u
.rdnSequence
.len
- 1) *
678 sizeof(name
->u
.rdnSequence
.val
[0]));
679 name
->u
.rdnSequence
.val
[0] = rdn
;
683 HX509_LIB_FUNCTION
int HX509_LIB_CALL
684 hx509_empty_name(hx509_context context
, hx509_name
*name
)
686 if ((*name
= calloc(1, sizeof(**name
))) == NULL
) {
687 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
690 (*name
)->der_name
.element
= choice_Name_rdnSequence
;
691 (*name
)->der_name
.u
.rdnSequence
.val
= 0;
692 (*name
)->der_name
.u
.rdnSequence
.len
= 0;
697 * Parse a string into a hx509 name object.
699 * @param context A hx509 context.
700 * @param str a string to parse.
701 * @param name the resulting object, NULL in case of error.
703 * @return An hx509 error code, see hx509_get_error_string().
705 * @ingroup hx509_name
708 HX509_LIB_FUNCTION
int HX509_LIB_CALL
709 hx509_parse_name(hx509_context context
, const char *str
, hx509_name
*name
)
718 n
= calloc(1, sizeof(*n
));
720 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
724 n
->der_name
.element
= choice_Name_rdnSequence
;
728 while (p
!= NULL
&& *p
!= '\0') {
743 ret
= HX509_PARSING_NAME_FAILED
;
744 hx509_set_error_string(context
, 0, ret
, "missing = in %s", p
);
748 ret
= HX509_PARSING_NAME_FAILED
;
749 hx509_set_error_string(context
, 0, ret
,
750 "missing name before = in %s", p
);
754 if ((size_t)(q
- p
) > len
) {
755 ret
= HX509_PARSING_NAME_FAILED
;
756 hx509_set_error_string(context
, 0, ret
, " = after , in %s", p
);
760 ret
= stringtooid(p
, q
- p
, &oid
);
762 ret
= HX509_PARSING_NAME_FAILED
;
763 hx509_set_error_string(context
, 0, ret
,
764 "unknown type: %.*s", (int)(q
- p
), p
);
769 size_t pstr_len
= len
- (q
- p
) - 1;
770 const char *pstr
= p
+ (q
- p
) + 1;
773 r
= malloc(pstr_len
+ 1);
777 hx509_set_error_string(context
, 0, ret
, "out of memory");
780 memcpy(r
, pstr
, pstr_len
);
783 ret
= _hx509_name_modify(context
, &n
->der_name
, 0, &oid
, r
);
797 return HX509_NAME_MALFORMED
;
801 * Copy a hx509 name object.
803 * @param context A hx509 cotext.
804 * @param from the name to copy from
805 * @param to the name to copy to
807 * @return An hx509 error code, see hx509_get_error_string().
809 * @ingroup hx509_name
812 HX509_LIB_FUNCTION
int HX509_LIB_CALL
813 hx509_name_copy(hx509_context context
, const hx509_name from
, hx509_name
*to
)
817 *to
= calloc(1, sizeof(**to
));
820 ret
= copy_Name(&from
->der_name
, &(*to
)->der_name
);
830 * Convert a hx509_name into a Name.
832 * @param from the name to copy from
833 * @param to the name to copy to
835 * @return An hx509 error code, see hx509_get_error_string().
837 * @ingroup hx509_name
840 HX509_LIB_FUNCTION
int HX509_LIB_CALL
841 hx509_name_to_Name(const hx509_name from
, Name
*to
)
843 return copy_Name(&from
->der_name
, to
);
846 HX509_LIB_FUNCTION
int HX509_LIB_CALL
847 hx509_name_normalize(hx509_context context
, hx509_name name
)
853 * Expands variables in the name using env. Variables are on the form
854 * ${name}. Useful when dealing with certificate templates.
856 * @param context A hx509 cotext.
857 * @param name the name to expand.
858 * @param env environment variable to expand.
860 * @return An hx509 error code, see hx509_get_error_string().
862 * @ingroup hx509_name
865 HX509_LIB_FUNCTION
int HX509_LIB_CALL
866 hx509_name_expand(hx509_context context
,
870 Name
*n
= &name
->der_name
;
872 int bounds_check
= 1;
877 if (n
->element
!= choice_Name_rdnSequence
) {
878 hx509_set_error_string(context
, 0, EINVAL
, "RDN not of supported type");
882 for (i
= 0 ; i
< n
->u
.rdnSequence
.len
; i
++) {
883 for (j
= 0; j
< n
->u
.rdnSequence
.val
[i
].len
; j
++) {
884 /** Only UTF8String rdnSequence names are allowed */
886 THIS SHOULD REALLY BE:
887 COMP = n->u.rdnSequence.val[i].val[j];
888 normalize COMP to utf8
889 check if there are variables
891 convert back to orignal format, store in COMP
892 free normalized utf8 string
894 DirectoryString
*ds
= &n
->u
.rdnSequence
.val
[i
].val
[j
].value
;
895 heim_oid
*type
= &n
->u
.rdnSequence
.val
[i
].val
[j
].type
;
896 const char *sval
= NULL
;
899 struct rk_strpool
*strpool
= NULL
;
901 switch (ds
->element
) {
902 case choice_DirectoryString_utf8String
:
903 sval
= ds
->u
.utf8String
;
905 case choice_DirectoryString_teletexString
:
906 sval
= ds
->u
.utf8String
;
908 case choice_DirectoryString_ia5String
:
909 s
= strndup(ds
->u
.ia5String
.data
,
910 ds
->u
.ia5String
.length
);
912 case choice_DirectoryString_printableString
:
913 s
= strndup(ds
->u
.printableString
.data
,
914 ds
->u
.printableString
.length
);
916 case choice_DirectoryString_universalString
:
917 hx509_set_error_string(context
, 0, ENOTSUP
, "UniversalString not supported");
919 case choice_DirectoryString_bmpString
:
920 hx509_set_error_string(context
, 0, ENOTSUP
, "BMPString not supported");
923 if (sval
== NULL
&& s
== NULL
)
924 return hx509_enomem(context
);
928 p
= strstr(sval
, "${");
930 strpool
= rk_strpoolprintf(strpool
, "%.*s", (int)(p
- sval
), sval
);
931 if (strpool
== NULL
) {
932 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
942 /* expand variables */
946 hx509_set_error_string(context
, 0, EINVAL
, "missing }");
947 rk_strpoolfree(strpool
);
951 value
= hx509_env_lfind(context
, env
, p
, p2
- p
);
953 hx509_set_error_string(context
, 0, EINVAL
,
954 "variable %.*s missing",
956 rk_strpoolfree(strpool
);
959 strpool
= rk_strpoolprintf(strpool
, "%s", value
);
960 if (strpool
== NULL
) {
961 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
966 p
= strstr(p2
, "${");
968 strpool
= rk_strpoolprintf(strpool
, "%.*s",
971 strpool
= rk_strpoolprintf(strpool
, "%s", p2
);
972 if (strpool
== NULL
) {
973 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
980 if ((s
= rk_strpoolcollect(strpool
)) == NULL
) {
981 hx509_set_error_string(context
, 0, ENOMEM
, "out of memory");
985 /* Check upper bounds! */
986 if ((max_bytes
= oidtomaxlen(type
)) && strlen(s
) > max_bytes
)
989 switch (ds
->element
) {
991 case choice_DirectoryString_utf8String
:
992 free(ds
->u
.utf8String
);
993 ds
->u
.utf8String
= s
;
995 case choice_DirectoryString_teletexString
:
996 free(ds
->u
.teletexString
);
997 ds
->u
.teletexString
= s
;
1000 /* Length and pointer */
1001 case choice_DirectoryString_ia5String
:
1002 free(ds
->u
.ia5String
.data
);
1003 ds
->u
.ia5String
.data
= s
;
1004 ds
->u
.ia5String
.length
= strlen(s
);
1006 case choice_DirectoryString_printableString
:
1007 free(ds
->u
.printableString
.data
);
1008 ds
->u
.printableString
.data
= s
;
1009 ds
->u
.printableString
.length
= strlen(s
);
1012 break; /* Handled above */
1018 if (!bounds_check
) {
1019 hx509_set_error_string(context
, 0, HX509_PARSING_NAME_FAILED
,
1020 "some expanded RDNs are too long");
1021 return HX509_PARSING_NAME_FAILED
;
1027 * Free a hx509 name object, upond return *name will be NULL.
1029 * @param name a hx509 name object to be freed.
1031 * @ingroup hx509_name
1034 HX509_LIB_FUNCTION
void HX509_LIB_CALL
1035 hx509_name_free(hx509_name
*name
)
1037 free_Name(&(*name
)->der_name
);
1038 memset(*name
, 0, sizeof(**name
));
1044 * Convert a DER encoded name info a string.
1046 * @param data data to a DER/BER encoded name
1047 * @param length length of data
1048 * @param str the resulting string, is NULL on failure.
1050 * @return An hx509 error code, see hx509_get_error_string().
1052 * @ingroup hx509_name
1055 HX509_LIB_FUNCTION
int HX509_LIB_CALL
1056 hx509_unparse_der_name(const void *data
, size_t length
, char **str
)
1063 ret
= decode_Name(data
, length
, &name
, NULL
);
1066 ret
= _hx509_Name_to_string(&name
, str
);
1072 * Convert a hx509_name object to DER encoded name.
1074 * @param name name to concert
1075 * @param os data to a DER encoded name, free the resulting octet
1076 * string with hx509_xfree(os->data).
1078 * @return An hx509 error code, see hx509_get_error_string().
1080 * @ingroup hx509_name
1083 HX509_LIB_FUNCTION
int HX509_LIB_CALL
1084 hx509_name_binary(const hx509_name name
, heim_octet_string
*os
)
1089 ASN1_MALLOC_ENCODE(Name
, os
->data
, os
->length
, &name
->der_name
, &size
, ret
);
1092 if (os
->length
!= size
)
1093 _hx509_abort("internal ASN.1 encoder error");
1098 HX509_LIB_FUNCTION
int HX509_LIB_CALL
1099 _hx509_unparse_Name(const Name
*aname
, char **str
)
1104 ret
= _hx509_name_from_Name(aname
, &name
);
1108 ret
= hx509_name_to_string(name
, str
);
1109 hx509_name_free(&name
);
1114 * Check if a name is empty.
1116 * @param name the name to check if its empty/null.
1118 * @return non zero if the name is empty/null.
1120 * @ingroup hx509_name
1123 HX509_LIB_FUNCTION
int HX509_LIB_CALL
1124 hx509_name_is_null_p(const hx509_name name
)
1126 return name
->der_name
.element
== choice_Name_rdnSequence
&&
1127 name
->der_name
.u
.rdnSequence
.len
== 0;
1131 _hx509_unparse_PermanentIdentifier(hx509_context context
,
1132 struct rk_strpool
**strpool
,
1135 PermanentIdentifier pi
;
1137 const char *pid
= "";
1141 ret
= decode_PermanentIdentifier(value
->data
, value
->length
, &pi
, &len
);
1142 if (ret
== 0 && pi
.assigner
&&
1143 der_print_heim_oid(pi
.assigner
, '.', &s
) != 0)
1144 ret
= hx509_enomem(context
);
1145 if (pi
.identifierValue
&& *pi
.identifierValue
)
1146 pid
= *pi
.identifierValue
;
1148 (*strpool
= rk_strpoolprintf(*strpool
, "%s:%s", s
? s
: "", pid
)) == NULL
)
1149 ret
= hx509_enomem(context
);
1150 free_PermanentIdentifier(&pi
);
1153 rk_strpoolfree(*strpool
);
1154 *strpool
= rk_strpoolprintf(NULL
,
1155 "<error-decoding-PermanentIdentifier");
1156 hx509_set_error_string(context
, 0, ret
,
1157 "Failed to decode PermanentIdentifier");
1163 _hx509_unparse_HardwareModuleName(hx509_context context
,
1164 struct rk_strpool
**strpool
,
1167 HardwareModuleName hm
;
1172 ret
= decode_HardwareModuleName(value
->data
, value
->length
, &hm
, &len
);
1173 if (ret
== 0 && hm
.hwSerialNum
.length
> 256)
1174 hm
.hwSerialNum
.length
= 256;
1176 ret
= der_print_heim_oid(&hm
.hwType
, '.', &s
);
1178 *strpool
= rk_strpoolprintf(*strpool
, "%s:%.*s%s", s
,
1179 (int)hm
.hwSerialNum
.length
,
1180 (char *)hm
.hwSerialNum
.data
,
1181 value
->length
== len
? "" : ", <garbage>");
1182 if (*strpool
== NULL
)
1183 ret
= hx509_enomem(context
);
1185 free_HardwareModuleName(&hm
);
1188 rk_strpoolfree(*strpool
);
1189 *strpool
= rk_strpoolprintf(NULL
,
1190 "<error-decoding-HardwareModuleName");
1191 hx509_set_error_string(context
, 0, ret
,
1192 "Failed to decode HardwareModuleName");
1198 * This necessarily duplicates code from libkrb5, and has to unless we move
1199 * common code here or to lib/roken for it. We do have slightly different
1200 * needs (e.g., we want space quoted, and we want to indicate whether we saw
1201 * trailing garbage, we have no need for flags, no special realm treatment,
1202 * etc) than the corresponding code in libkrb5, so for now we duplicate this
1205 * The relevant RFCs here are RFC1964 for the string representation of Kerberos
1206 * principal names, and RFC4556 for the KRB5PrincipalName ASN.1 type (Kerberos
1207 * lacks such a type because on the wire the name and realm are sent
1208 * separately as a form of cheap compression).
1210 * Note that we cannot handle embedded NULs because of Heimdal's representation
1211 * of ASN.1 strings as C strings.
1214 _hx509_unparse_KRB5PrincipalName(hx509_context context
,
1215 struct rk_strpool
**strpool
,
1218 KRB5PrincipalName kn
;
1222 ret
= decode_KRB5PrincipalName(value
->data
, value
->length
, &kn
, &len
);
1224 (*strpool
= _hx509_unparse_kerberos_name(*strpool
, &kn
)) == NULL
)
1225 ret
= hx509_enomem(context
);
1226 free_KRB5PrincipalName(&kn
);
1227 if (ret
== 0 && (value
->length
!= len
) &&
1228 (*strpool
= rk_strpoolprintf(*strpool
, " <garbage>")) == NULL
)
1229 ret
= hx509_enomem(context
);
1231 rk_strpoolfree(*strpool
);
1232 *strpool
= rk_strpoolprintf(NULL
,
1233 "<error-decoding-PrincipalName");
1234 hx509_set_error_string(context
, 0, ret
,
1235 "Failed to decode PermanentIdentifier");
1241 _hx509_unparse_kerberos_name(struct rk_strpool
*strpool
, KRB5PrincipalName
*kn
)
1243 static const char comp_quotable_chars
[] = " \n\t\b\\/@";
1244 static const char realm_quotable_chars
[] = " \n\t\b\\@";
1246 size_t i
, k
, len
, plen
;
1249 for (i
= 0; i
< kn
->principalName
.name_string
.len
; i
++) {
1250 s
= kn
->principalName
.name_string
.val
[i
];
1254 strpool
= rk_strpoolprintf(strpool
, "/");
1257 for (k
= 0; k
< len
; s
+= plen
, k
+= plen
) {
1260 plen
= strcspn(s
, comp_quotable_chars
);
1262 strpool
= rk_strpoolprintf(strpool
, "%.*s", (int)plen
, s
);
1263 if (k
+ plen
>= len
)
1265 switch ((c
= s
[plen
++])) {
1266 case '\n': strpool
= rk_strpoolprintf(strpool
, "\\n"); break;
1267 case '\t': strpool
= rk_strpoolprintf(strpool
, "\\t"); break;
1268 case '\b': strpool
= rk_strpoolprintf(strpool
, "\\b"); break;
1269 /* default -> '@', ' ', '\\', or '/' */
1270 default: strpool
= rk_strpoolprintf(strpool
, "\\%c", c
); break;
1276 strpool
= rk_strpoolprintf(strpool
, "@");
1279 len
= strlen(kn
->realm
);
1280 for (k
= 0; k
< len
; s
+= plen
, k
+= plen
) {
1283 plen
= strcspn(s
, realm_quotable_chars
);
1285 strpool
= rk_strpoolprintf(strpool
, "%.*s", (int)plen
, s
);
1286 if (k
+ plen
>= len
)
1288 switch ((c
= s
[plen
++])) {
1289 case '\n': strpool
= rk_strpoolprintf(strpool
, "\\n"); break;
1290 case '\t': strpool
= rk_strpoolprintf(strpool
, "\\t"); break;
1291 case '\b': strpool
= rk_strpoolprintf(strpool
, "\\b"); break;
1292 /* default -> '@', ' ', or '\\' */
1293 default: strpool
= rk_strpoolprintf(strpool
, "\\%c", c
); break;
1300 _hx509_unparse_utf8_string_name(hx509_context context
,
1301 struct rk_strpool
**strpool
,
1308 ret
= decode_PKIXXmppAddr(value
->data
, value
->length
, &us
, &size
);
1310 (*strpool
= rk_strpoolprintf(*strpool
, "%s", us
)) == NULL
)
1311 ret
= hx509_enomem(context
);
1313 rk_strpoolfree(*strpool
);
1314 *strpool
= rk_strpoolprintf(NULL
,
1315 "<error-decoding-UTF8String-SAN>");
1316 hx509_set_error_string(context
, 0, ret
,
1317 "Failed to decode UTF8String SAN");
1319 free_PKIXXmppAddr(&us
);
1324 _hx509_unparse_ia5_string_name(hx509_context context
,
1325 struct rk_strpool
**strpool
,
1332 ret
= decode_SRVName(value
->data
, value
->length
, &us
, &size
);
1334 rk_strpoolfree(*strpool
);
1335 *strpool
= rk_strpoolprintf(NULL
,
1336 "<error-decoding-IA5String-SAN>");
1337 hx509_set_error_string(context
, 0, ret
,
1338 "Failed to decode UTF8String SAN");
1341 *strpool
= rk_strpoolprintf(*strpool
, "%.*s",
1342 (int)us
.length
, (char *)us
.data
);
1347 typedef int (*other_unparser_f
)(hx509_context
,
1348 struct rk_strpool
**,
1352 const heim_oid
*oid
;
1353 const char *friendly_name
;
1356 { &asn1_oid_id_pkinit_san
,
1357 "KerberosPrincipalName",
1358 _hx509_unparse_KRB5PrincipalName
},
1359 { &asn1_oid_id_pkix_on_permanentIdentifier
,
1360 "PermanentIdentifier",
1361 _hx509_unparse_PermanentIdentifier
},
1362 { &asn1_oid_id_on_hardwareModuleName
,
1363 "HardwareModuleName",
1364 _hx509_unparse_HardwareModuleName
},
1365 { &asn1_oid_id_pkix_on_xmppAddr
,
1367 _hx509_unparse_utf8_string_name
},
1368 { &asn1_oid_id_pkinit_ms_san
,
1369 "MSFTKerberosPrincipalName",
1370 _hx509_unparse_utf8_string_name
},
1371 { &asn1_oid_id_pkix_on_dnsSRV
,
1373 _hx509_unparse_ia5_string_name
},
1377 * Unparse the hx509 name in name into a string.
1379 * @param name the name to print
1380 * @param str an allocated string returns the name in string form
1382 * @return An hx509 error code, see hx509_get_error_string().
1384 * @ingroup hx509_name
1387 HX509_LIB_FUNCTION
int HX509_LIB_CALL
1388 hx509_general_name_unparse(GeneralName
*name
, char **str
)
1390 hx509_context context
;
1393 if ((ret
= hx509_context_init(&context
)))
1395 return hx509_general_name_unparse2(context
, name
, str
);
1399 * Unparse the hx509 name in name into a string.
1401 * @param context hx509 library context
1402 * @param name the name to print
1403 * @param str an allocated string returns the name in string form
1405 * @return An hx509 error code, see hx509_get_error_string().
1407 * @ingroup hx509_name
1410 HX509_LIB_FUNCTION
int HX509_LIB_CALL
1411 hx509_general_name_unparse2(hx509_context context
,
1415 struct rk_strpool
*strpool
= NULL
;
1420 switch (name
->element
) {
1421 case choice_GeneralName_otherName
: {
1425 ret
= hx509_oid_sprint(&name
->u
.otherName
.type_id
, &oid
);
1427 strpool
= rk_strpoolprintf(strpool
, "otherName: %s ", oid
);
1428 if (strpool
== NULL
)
1431 for (i
= 0; ret
== 0 && i
< sizeof(o_unparsers
)/sizeof(o_unparsers
[0]); i
++) {
1432 if (der_heim_oid_cmp(&name
->u
.otherName
.type_id
,
1433 o_unparsers
[i
].oid
))
1435 strpool
= rk_strpoolprintf(strpool
, "%s ",o_unparsers
[i
].friendly_name
);
1436 if (strpool
== NULL
)
1439 ret
= o_unparsers
[i
].f(context
, &strpool
, &name
->u
.otherName
.value
);
1442 if (ret
== 0 && i
== sizeof(o_unparsers
)/sizeof(o_unparsers
[0])) {
1443 strpool
= rk_strpoolprintf(strpool
, "<unknown-other-name-type>");
1449 case choice_GeneralName_rfc822Name
:
1450 strpool
= rk_strpoolprintf(strpool
, "rfc822Name: %.*s",
1451 (int)name
->u
.rfc822Name
.length
,
1452 (char *)name
->u
.rfc822Name
.data
);
1454 case choice_GeneralName_dNSName
:
1455 strpool
= rk_strpoolprintf(strpool
, "dNSName: %.*s",
1456 (int)name
->u
.dNSName
.length
,
1457 (char *)name
->u
.dNSName
.data
);
1459 case choice_GeneralName_directoryName
: {
1462 memset(&dir
, 0, sizeof(dir
));
1463 dir
.element
= (enum Name_enum
)name
->u
.directoryName
.element
;
1464 dir
.u
.rdnSequence
= name
->u
.directoryName
.u
.rdnSequence
;
1465 ret
= _hx509_unparse_Name(&dir
, &s
);
1468 strpool
= rk_strpoolprintf(strpool
, "directoryName: %s", s
);
1472 case choice_GeneralName_uniformResourceIdentifier
:
1473 strpool
= rk_strpoolprintf(strpool
, "URI: %.*s",
1474 (int)name
->u
.uniformResourceIdentifier
.length
,
1475 (char *)name
->u
.uniformResourceIdentifier
.data
);
1477 case choice_GeneralName_iPAddress
: {
1478 unsigned char *a
= name
->u
.iPAddress
.data
;
1480 strpool
= rk_strpoolprintf(strpool
, "IPAddress: ");
1481 if (strpool
== NULL
)
1483 if (name
->u
.iPAddress
.length
== 4)
1484 strpool
= rk_strpoolprintf(strpool
, "%d.%d.%d.%d",
1485 a
[0], a
[1], a
[2], a
[3]);
1486 else if (name
->u
.iPAddress
.length
== 16)
1487 strpool
= rk_strpoolprintf(strpool
,
1488 "%02X:%02X:%02X:%02X:"
1489 "%02X:%02X:%02X:%02X:"
1490 "%02X:%02X:%02X:%02X:"
1491 "%02X:%02X:%02X:%02X",
1492 a
[0], a
[1], a
[2], a
[3],
1493 a
[4], a
[5], a
[6], a
[7],
1494 a
[8], a
[9], a
[10], a
[11],
1495 a
[12], a
[13], a
[14], a
[15]);
1497 strpool
= rk_strpoolprintf(strpool
,
1498 "unknown IP address of length %lu",
1499 (unsigned long)name
->u
.iPAddress
.length
);
1502 case choice_GeneralName_registeredID
: {
1504 hx509_oid_sprint(&name
->u
.registeredID
, &oid
);
1507 strpool
= rk_strpoolprintf(strpool
, "registeredID: %s", oid
);
1514 if (strpool
== NULL
||
1515 (*str
= rk_strpoolcollect(strpool
)) == NULL
)