1/*
2 * The contents of this file are subject to the Mozilla Public
3 * License Version 1.1 (the "License"); you may not use this file
4 * except in compliance with the License. You may obtain a copy of
5 * the License at http://www.mozilla.org/MPL/
6 *
7 * Software distributed under the License is distributed on an "AS
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
9 * implied. See the License for the specific language governing
10 * rights and limitations under the License.
11 *
12 * The Original Code is the Netscape security libraries.
13 *
14 * The Initial Developer of the Original Code is Netscape
15 * Communications Corporation.  Portions created by Netscape are
16 * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the
22 * terms of the GNU General Public License Version 2 or later (the
23 * "GPL"), in which case the provisions of the GPL are applicable
24 * instead of those above.  If you wish to allow use of your
25 * version of this file only under the terms of the GPL and not to
26 * allow others to use your version of this file under the MPL,
27 * indicate your decision by deleting the provisions above and
28 * replace them with the notice and other provisions required by
29 * the GPL.  If you do not delete the provisions above, a recipient
30 * may use your version of this file under either the MPL or the
31 * GPL.
32 *
33 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
34 * Use is subject to license terms.
35 *
36 * Copyright 2018, Joyent, Inc.
37 *
38 * File: rdn_parser.c
39 */
40
41#include <strings.h>
42#include <stdlib.h>
43#include <kmfapi.h>
44#include <kmfapiP.h>
45#include <ber_der.h>
46#include <rdn_parser.h>
47#include <stdio.h>
48#include <values.h>
49#include <libcustr.h>
50
51/*
52 * The order here is important.  The OIDs are arranged in order of
53 * significance.  The CN is the most specific value, the C (country)
54 * is less specific, etc.  Add to this list with care.
55 */
56static const struct NameToKind name2kinds[] = {
57{ "CN",		OID_AVA_COMMON_NAME,	(KMF_OID *)&KMFOID_CommonName},
58{ "SN",		OID_AVA_SURNAME,	(KMF_OID *)&KMFOID_Surname},
59{ "GN",		OID_AVA_GIVEN_NAME,	(KMF_OID *)&KMFOID_GivenName},
60{ "emailAddress", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
61{ "E",		OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
62{ "MAIL",	OID_RFC1274_MAIL,	(KMF_OID *)&KMFOID_RFC822mailbox},
63{ "STREET",	OID_AVA_STREET_ADDRESS, (KMF_OID *)&KMFOID_StreetAddress},
64{ "UID",	OID_RFC1274_UID,	(KMF_OID *)&KMFOID_userid},
65{ "OU",		OID_AVA_ORGANIZATIONAL_UNIT_NAME,
66			(KMF_OID *)&KMFOID_OrganizationalUnitName},
67{ "O",		OID_AVA_ORGANIZATION_NAME, (KMF_OID *)&KMFOID_OrganizationName},
68{ "L",		OID_AVA_LOCALITY,	(KMF_OID *)&KMFOID_LocalityName},
69{ "ST",		OID_AVA_STATE_OR_PROVINCE,
70	(KMF_OID *)&KMFOID_StateProvinceName},
71{ "C",		OID_AVA_COUNTRY_NAME,	(KMF_OID *)&KMFOID_CountryName},
72{ "DC",		OID_AVA_DC,		(KMF_OID *)&KMFOID_domainComponent},
73{ 0,		OID_UNKNOWN, NULL}
74};
75
76static KMF_BOOL
77IsPrintable(unsigned char *data, unsigned len)
78{
79	unsigned char ch, *end;
80
81	end = data + len;
82	while (data < end) {
83		ch = *data++;
84		if (!IS_PRINTABLE(ch)) {
85			return (B_FALSE);
86		}
87	}
88	return (B_TRUE);
89}
90
91static KMF_BOOL
92Is7Bit(unsigned char *data, unsigned len)
93{
94	unsigned char ch, *end;
95
96	end = data + len;
97	while (data < end) {
98		ch = *data++;
99		if ((ch & 0x80)) {
100			return (B_FALSE);
101		}
102	}
103	return (B_TRUE);
104}
105
106static void
107skipSpace(char **pbp, char *endptr)
108{
109	char *bp = *pbp;
110	while (bp < endptr && OPTIONAL_SPACE(*bp)) {
111		bp++;
112	}
113	*pbp = bp;
114}
115
116static KMF_RETURN
117scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize)
118{
119	char *bp, *tagBufp;
120	int taglen;
121
122	if (tagBufSize <= 0)
123		return (KMF_ERR_INTERNAL);
124
125	/* skip optional leading space */
126	skipSpace(pbp, endptr);
127	if (*pbp == endptr) {
128		/* nothing left */
129		return (KMF_ERR_RDN_PARSER);
130	}
131
132	/* fill tagBuf */
133	taglen = 0;
134	bp = *pbp;
135	tagBufp = tagBuf;
136	while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
137		if (++taglen >= tagBufSize) {
138			*pbp = bp;
139			return (KMF_ERR_RDN_PARSER);
140		}
141		*tagBufp++ = *bp++;
142	}
143	/* null-terminate tagBuf -- guaranteed at least one space left */
144	*tagBufp++ = 0;
145	*pbp = bp;
146
147	/*
148	 * skip trailing spaces till we hit something - should be
149	 * an equal sign
150	 */
151	skipSpace(pbp, endptr);
152	if (*pbp == endptr) {
153		/* nothing left */
154		return (KMF_ERR_RDN_PARSER);
155	}
156	if (**pbp != C_EQUAL) {
157		/* should be an equal sign */
158		return (KMF_ERR_RDN_PARSER);
159	}
160	/* skip over the equal sign */
161	(*pbp)++;
162
163	return (KMF_OK);
164}
165
166static KMF_RETURN
167scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize)
168{
169	char *bp, *valBufp;
170	int vallen;
171	boolean_t isQuoted;
172
173	if (valBufSize <= 0)
174		return (KMF_ERR_INTERNAL);
175
176	/* skip optional leading space */
177	skipSpace(pbp, endptr);
178	if (*pbp == endptr) {
179		/* nothing left */
180		return (KMF_ERR_RDN_PARSER);
181	}
182
183	bp = *pbp;
184
185	/* quoted? */
186	if (*bp == C_DOUBLE_QUOTE) {
187		isQuoted = B_TRUE;
188		/* skip over it */
189		bp++;
190	} else {
191		isQuoted = B_FALSE;
192	}
193
194	valBufp = valBuf;
195	vallen = 0;
196	while (bp < endptr) {
197		char c = *bp;
198		if (c == C_BACKSLASH) {
199			/* escape character */
200			bp++;
201			if (bp >= endptr) {
202				/*
203				 * escape charater must appear with paired char
204				 */
205				*pbp = bp;
206				return (KMF_ERR_RDN_PARSER);
207			}
208		} else if (!isQuoted && SPECIAL_CHAR(c)) {
209			/* unescaped special and not within quoted value */
210			break;
211		} else if (c == C_DOUBLE_QUOTE) {
212			/* reached unescaped double quote */
213			break;
214		}
215		/* append character */
216		vallen++;
217		if (vallen >= valBufSize) {
218			*pbp = bp;
219			return (KMF_ERR_RDN_PARSER);
220		}
221		*valBufp++ = *bp++;
222	}
223
224	/* stip trailing spaces from unquoted values */
225	if (!isQuoted) {
226		if (valBufp > valBuf) {
227			valBufp--;
228			while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) {
229				valBufp--;
230			}
231			valBufp++;
232		}
233	}
234
235	if (isQuoted) {
236		/* insist that we stopped on a double quote */
237		if (*bp != C_DOUBLE_QUOTE) {
238			*pbp = bp;
239			return (KMF_ERR_RDN_PARSER);
240		}
241		/* skip over the quote and skip optional space */
242		bp++;
243		skipSpace(&bp, endptr);
244	}
245
246	*pbp = bp;
247
248	if (valBufp == valBuf) {
249		/* empty value -- not allowed */
250		return (KMF_ERR_RDN_PARSER);
251	}
252
253	/* null-terminate valBuf -- guaranteed at least one space left */
254	*valBufp++ = 0;
255
256	return (KMF_OK);
257}
258
259static KMF_RETURN
260CreateRDN(KMF_X509_TYPE_VALUE_PAIR *ava, KMF_X509_RDN *newrdn)
261{
262	/* Each RDN has 1 AttrTypeAndValue */
263	(void) memset(newrdn, 0, sizeof (KMF_X509_RDN));
264	newrdn->numberOfPairs = 1;
265	newrdn->AttributeTypeAndValue = ava;
266
267	return (KMF_OK);
268}
269
270static KMF_RETURN
271copy_oid(KMF_OID *dst, KMF_OID *src)
272{
273	KMF_RETURN ret = KMF_OK;
274
275	if (dst == NULL || src == NULL)
276		return (KMF_ERR_BAD_PARAMETER);
277
278	dst->Data = malloc(src->Length);
279	if (dst->Data == NULL)
280		return (KMF_ERR_MEMORY);
281
282	dst->Length = src->Length;
283	(void) memcpy(dst->Data, src->Data, src->Length);
284
285	return (ret);
286}
287
288static KMF_RETURN
289CreateAVA(KMF_OID *oid, int valueType, char *value,
290    KMF_X509_TYPE_VALUE_PAIR **newava)
291{
292	int rv = KMF_OK;
293	KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
294
295	*newava = NULL;
296	ava = (KMF_X509_TYPE_VALUE_PAIR*) malloc(
297	    sizeof (KMF_X509_TYPE_VALUE_PAIR));
298	if (ava == NULL) {
299		return (KMF_ERR_MEMORY);
300	} else {
301		(void) memset(ava, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR));
302		ava->valueType = valueType;
303		ava->value.Data = malloc(strlen(value));
304		if (ava->value.Data == NULL) {
305			free(ava);
306			return (KMF_ERR_MEMORY);
307		}
308		(void) memcpy(ava->value.Data, value, strlen(value));
309		ava->value.Length = strlen(value);
310
311		rv = copy_oid(&ava->type, oid);
312		if (rv != KMF_OK) {
313			/* Illegal AVA type */
314			free(ava->value.Data);
315			free(ava);
316			return (rv);
317		}
318	}
319	*newava = ava;
320
321	return (rv);
322}
323
324static KMF_RETURN
325ParseRdnAttribute(char **pbp, char *endptr, boolean_t singleAVA,
326    KMF_X509_TYPE_VALUE_PAIR **a)
327{
328	KMF_RETURN rv;
329	const struct NameToKind *n2k;
330	int vt;
331	int valLen;
332	char *bp;
333
334	char tagBuf[32];
335	char valBuf[384];
336
337	rv = scanTag(pbp, endptr, tagBuf, sizeof (tagBuf));
338	if (rv != KMF_OK)
339		return (rv);
340	rv = scanVal(pbp, endptr, valBuf, sizeof (valBuf));
341	if (rv != KMF_OK)
342		return (rv);
343
344	/* insist that if we haven't finished we've stopped on a separator */
345	bp = *pbp;
346	if (bp < endptr) {
347		if (singleAVA || (*bp != ',' && *bp != ';')) {
348			*pbp = bp;
349			return (KMF_ERR_RDN_ATTR);
350		}
351		/* ok, skip over separator */
352		bp++;
353	}
354	*pbp = bp;
355
356	for (n2k = name2kinds; n2k->name; n2k++) {
357		if (strcasecmp(n2k->name, tagBuf) == 0) {
358			valLen = strlen(valBuf);
359			if (n2k->kind == OID_AVA_COUNTRY_NAME) {
360				vt = BER_PRINTABLE_STRING;
361				if (valLen != 2) {
362					return (KMF_ERR_RDN_ATTR);
363				}
364				if (!IsPrintable((unsigned char *) valBuf, 2)) {
365					return (KMF_ERR_RDN_ATTR);
366				}
367			} else if ((n2k->kind == OID_PKCS9_EMAIL_ADDRESS) ||
368			    (n2k->kind == OID_RFC1274_MAIL)) {
369				vt = BER_IA5STRING;
370			} else {
371				/*
372				 * Hack -- for rationale see X.520
373				 * DirectoryString defn
374				 */
375				if (IsPrintable((unsigned char *)valBuf,
376				    valLen)) {
377					vt = BER_PRINTABLE_STRING;
378				} else if (Is7Bit((unsigned char *)valBuf,
379				    valLen)) {
380					vt = BER_T61STRING;
381				}
382			}
383			rv = CreateAVA(n2k->OID, vt, (char *)valBuf, a);
384			return (rv);
385		}
386	}
387	/* matched no kind -- invalid tag */
388	return (KMF_ERR_RDN_ATTR);
389}
390
391static int
392rdnavcompare(const void *a, const void *b)
393{
394	KMF_X509_RDN *r1, *r2;
395	KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
396	int i, p1, p2;
397	const struct NameToKind *n2k;
398	KMF_OID *oidrec;
399
400	r1 = (KMF_X509_RDN *)a;
401	r2 = (KMF_X509_RDN *)b;
402
403	av1 = r1->AttributeTypeAndValue;
404	av2 = r2->AttributeTypeAndValue;
405
406	p1 = p2 = MAXINT;
407	/*
408	 * The "Name2Kinds" list is ordered by significance.
409	 * Compare the "ranking" of each of the OIDs to determine
410	 * the result.
411	 */
412	for (n2k = name2kinds, i = 0;
413	    n2k->name && (p1 == MAXINT || p2 == MAXINT);
414	    n2k++, i++) {
415		oidrec = n2k->OID;
416		if (oidrec != NULL) {
417			if (IsEqualOid(&av1->type, oidrec))
418				p1 = i;
419			if (IsEqualOid(&av2->type, oidrec))
420				p2 = i;
421		}
422	}
423
424	if (p1 > p2)
425		return (-1);
426	else if (p1 < p2)
427		return (1);
428	else  /* If equal, treat as if it is less than */
429		return (1);
430}
431
432static KMF_RETURN
433ParseDistinguishedName(char *buf, int len, KMF_X509_NAME *name)
434{
435	KMF_RETURN rv = KMF_OK;
436	char *bp, *e;
437	KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
438	KMF_X509_RDN rdn;
439
440	(void) memset(name, 0, sizeof (KMF_X509_NAME));
441	e = buf + len;
442	bp = buf;
443	while (bp < e) {
444		rv = ParseRdnAttribute(&bp, e, B_FALSE, &ava);
445		if (rv != KMF_OK) goto loser;
446		rv = CreateRDN(ava, &rdn);
447		if (rv != KMF_OK) goto loser;
448		if (AddRDN(name, &rdn) != KMF_OK) goto loser;
449		skipSpace(&bp, e);
450	}
451
452	/*
453	 * Canonicalize the DN by sorting the elements
454	 * in little-endian order, as per RFC 1485:
455	 * "The name is presented/input in a little-endian
456	 * order (most significant component last)."
457	 */
458	qsort((void *)name->RelativeDistinguishedName,
459	    name->numberOfRDNs, sizeof (KMF_X509_RDN), rdnavcompare);
460
461	/* return result */
462	return (rv);
463
464loser:
465	kmf_free_dn(name);
466	return (rv);
467}
468
469static KMF_BOOL
470IsEqualData(KMF_DATA *d1, KMF_DATA *d2)
471{
472	return ((d1->Length == d2->Length) &&
473	    !memcmp(d1->Data, d2->Data, d1->Length));
474}
475
476/*
477 * Generic routine to compare 2 RDN structures.
478 *
479 * Because the ordering of the AV pairs may not be
480 * the same, we must compare each AV pair individually
481 *
482 * Return 0 if equal, 1 if not.
483 */
484int
485kmf_compare_rdns(KMF_X509_NAME *name1, KMF_X509_NAME *name2)
486{
487	int i, j;
488	boolean_t avfound;
489	KMF_X509_RDN *r1, *r2;
490	KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
491
492	if (name1 == NULL || name2 == NULL)
493		return (1);
494
495	if (name1->numberOfRDNs != name2->numberOfRDNs)
496		return (1);
497
498	for (i = 0; i < name1->numberOfRDNs; i++) {
499		r1 = (KMF_X509_RDN *)&name1->RelativeDistinguishedName[i];
500		av1 = (KMF_X509_TYPE_VALUE_PAIR *)r1->AttributeTypeAndValue;
501
502		avfound = FALSE;
503		for (j = 0; j < name2->numberOfRDNs && !avfound; j++) {
504			r2 = (KMF_X509_RDN *)
505			    &name2->RelativeDistinguishedName[j];
506			av2 = (KMF_X509_TYPE_VALUE_PAIR *)
507			    r2->AttributeTypeAndValue;
508
509			avfound = (IsEqualOid(&av1->type, &av2->type) &&
510			    IsEqualData(&av1->value, &av2->value));
511		}
512		/*
513		 * If the current AV from name1 was not found in name2,
514		 * we are done.
515		 */
516		if (!avfound)
517			return (1);
518	}
519
520	/* If we got this far, it must be a match */
521	return (0);
522}
523
524/*
525 * kmf_dn_parser
526 *
527 * Public interface for parsing a Distinguished name in
528 * human-readable format into a binary KMF_X509_NAME.
529 */
530KMF_RETURN
531kmf_dn_parser(char *string, KMF_X509_NAME *name)
532{
533	KMF_RETURN err;
534
535	if (string == NULL || name == NULL)
536		return (KMF_ERR_BAD_PARAMETER);
537
538	err = ParseDistinguishedName(string, (int)strlen(string), name);
539	return (err);
540}
541
542static const char hexdigits[] = "0123456789abcdef";
543
544static KMF_RETURN
545binvalue_to_string(KMF_DATA *data, custr_t *str)
546{
547	size_t i;
548	uchar_t c;
549
550	if (custr_appendc(str, '#') != 0)
551		return (KMF_ERR_MEMORY);
552
553	for (i = 0; i < data->Length; i++) {
554		c = data->Data[i];
555		if (custr_appendc(str, hexdigits[(c >> 4) & 0xf]) != 0 ||
556		    custr_appendc(str, hexdigits[(c & 0xf)]) != 0) {
557			return (KMF_ERR_MEMORY);
558		}
559	}
560
561	return (KMF_OK);
562}
563
564/*
565 * Convert an RDN value into a printable name with appropriate escaping.
566 * The rules are taken from RFC4514.  While it is dealing with LDAP
567 * distinguished names, both LDAP and x509 certificates are based on the
568 * same underlying ITU standards, and as far as I can determine, the same
569 * rules apply (or at least the rules for LDAP DNs apply the same to x509
570 * DNs).
571 */
572static KMF_RETURN
573value_to_string(KMF_DATA *data, custr_t *str)
574{
575	size_t i;
576	uchar_t c;
577
578	for (i = 0; i < data->Length; i++) {
579		c = data->Data[i];
580
581		/*
582		 * While technically not required, it is suggested that
583		 * printable non-ascii characters (e.g. multi-byte UTF-8
584		 * characters) are converted as escaped hex (as well as
585		 * unprintable characters).  AFAIK there is no one canonical
586		 * string representation (e.g. attribute names are case
587		 * insensitive, so 'CN=foo' and 'cn=foo' convert to the same
588		 * binary representation, but there is nothing to say if
589		 * either string form is canonical), so this shouldn't
590		 * pose a problem.
591		 */
592		if (c < ' ' || c >= 0x7f) {
593			/*
594			 * RFC4514 specifies the hex form in a DN string as
595			 * \{hex}{hex}. OpenSSL uses capitals for A-F so we
596			 * do the same.
597			 */
598			if (custr_append_printf(str, "\\%02hhX", c) != 0)
599				return (KMF_ERR_MEMORY);
600			continue;
601		}
602
603		switch (c) {
604		case '#':
605			/* Escape # if at the start of a value */
606			if (i != 0)
607				break;
608			/* FALLTHROUGH */
609		case ' ':
610			/* Escape ' ' if at the start or end of a value */
611			if (i != 0 && i + 1 != data->Length)
612				break;
613			/* FALLTHROUGH */
614		case '"':
615		case '+':
616		case ',':
617		case ';':
618		case '<':
619		case '>':
620		case '\\':
621			/* Escape these */
622			if (custr_appendc(str, '\\') != 0)
623				return (KMF_ERR_MEMORY);
624		}
625
626		if (custr_appendc(str, c) != 0)
627			return (KMF_ERR_MEMORY);
628	}
629
630	return (KMF_OK);
631}
632
633/*
634 * Translate an attribute/value pair into a string.  If the attribute OID
635 * is a well known OID (in name2kinds) we use the name instead of the OID.
636 */
637static KMF_RETURN
638ava_to_string(KMF_X509_TYPE_VALUE_PAIR *tvp, custr_t *str)
639{
640	KMF_OID *kind_oid;
641	KMF_OID *rdn_oid = &tvp->type;
642	const char *attr = NULL;
643	size_t i;
644	KMF_RETURN ret = KMF_OK;
645	boolean_t found = B_FALSE;
646
647	for (i = 0; name2kinds[i].name != NULL; i++) {
648		kind_oid = name2kinds[i].OID;
649
650		if (!IsEqualOid(kind_oid, rdn_oid))
651			continue;
652
653		attr = name2kinds[i].name;
654		found = B_TRUE;
655		break;
656	}
657
658	if (!found && (attr = kmf_oid_to_string(rdn_oid)) == NULL) {
659		ret = KMF_ERR_MEMORY;
660		goto done;
661	}
662	if (custr_append(str, attr) != 0) {
663		ret = KMF_ERR_MEMORY;
664		goto done;
665	}
666	if (custr_appendc(str, '=') != 0) {
667		ret = KMF_ERR_MEMORY;
668		goto done;
669	}
670
671	/*
672	 * RFC4514 indicates that an oid=value pair should have the value
673	 * printed as #xxxxxx.  In addition, we also print as a binary
674	 * value if the BER tag does not indicate the value is some sort
675	 * of printable string.
676	 */
677	switch (tvp->valueType) {
678	case BER_UTF8_STRING:
679	case BER_PRINTABLE_STRING:
680	case BER_T61STRING:
681	case BER_IA5STRING:
682		if (found) {
683			ret = value_to_string(&tvp->value, str);
684			break;
685		}
686		/*FALLTHROUGH*/
687	default:
688		ret = binvalue_to_string(&tvp->value, str);
689		break;
690	}
691
692done:
693	if (!found)
694		free((void *)attr);
695
696	return (ret);
697}
698
699static KMF_RETURN
700rdn_to_string(KMF_X509_RDN *rdn, custr_t *str)
701{
702	KMF_RETURN ret;
703	size_t i;
704
705	for (i = 0; i < rdn->numberOfPairs; i++) {
706		if (i > 0 && custr_appendc(str, '+') != 0)
707			return (KMF_ERR_MEMORY);
708
709		ret = ava_to_string(&rdn->AttributeTypeAndValue[i], str);
710		if (ret != KMF_OK)
711			return (ret);
712	}
713
714	return (KMF_OK);
715}
716
717/*
718 * kmf_dn_to_string
719 *
720 * Take a binary KMF_X509_NAME and convert it into a human readable string.
721 */
722KMF_RETURN
723kmf_dn_to_string(KMF_X509_NAME *name, char **string)
724{
725	custr_t *str = NULL;
726	KMF_RETURN err = KMF_OK;
727	size_t i;
728
729	if (name == NULL || string == NULL)
730		return (KMF_ERR_BAD_PARAMETER);
731
732	*string = NULL;
733
734	if (custr_alloc(&str) != 0)
735		return (KMF_ERR_MEMORY);
736
737	for (i = 0; i < name->numberOfRDNs; i++) {
738		KMF_X509_RDN *rdn = &name->RelativeDistinguishedName[i];
739
740		if (i > 0 && custr_append(str, ", ") != 0) {
741			err = KMF_ERR_MEMORY;
742			goto done;
743		}
744
745		if ((err = rdn_to_string(rdn, str)) != KMF_OK)
746			goto done;
747	}
748
749	if ((*string = strdup(custr_cstr(str))) == NULL)
750		err = KMF_ERR_MEMORY;
751
752done:
753	custr_free(str);
754	return (err);
755}
756