xref: /illumos-gate/usr/src/lib/libkmf/libkmf/common/rdn_parser.c (revision bdc560ab289d67ddebad9a2146fd36c2662d88b1)
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  */
56 static 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 
76 static KMF_BOOL
IsPrintable(unsigned char * data,unsigned len)77 IsPrintable(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 
91 static KMF_BOOL
Is7Bit(unsigned char * data,unsigned len)92 Is7Bit(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 
106 static void
skipSpace(char ** pbp,char * endptr)107 skipSpace(char **pbp, char *endptr)
108 {
109 	char *bp = *pbp;
110 	while (bp < endptr && OPTIONAL_SPACE(*bp)) {
111 		bp++;
112 	}
113 	*pbp = bp;
114 }
115 
116 static KMF_RETURN
scanTag(char ** pbp,char * endptr,char * tagBuf,int tagBufSize)117 scanTag(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 
166 static KMF_RETURN
scanVal(char ** pbp,char * endptr,char * valBuf,int valBufSize)167 scanVal(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 
259 static KMF_RETURN
CreateRDN(KMF_X509_TYPE_VALUE_PAIR * ava,KMF_X509_RDN * newrdn)260 CreateRDN(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 
270 static KMF_RETURN
copy_oid(KMF_OID * dst,KMF_OID * src)271 copy_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 
288 static KMF_RETURN
CreateAVA(KMF_OID * oid,int valueType,char * value,KMF_X509_TYPE_VALUE_PAIR ** newava)289 CreateAVA(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 
324 static KMF_RETURN
ParseRdnAttribute(char ** pbp,char * endptr,boolean_t singleAVA,KMF_X509_TYPE_VALUE_PAIR ** a)325 ParseRdnAttribute(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 
391 static int
rdnavcompare(const void * a,const void * b)392 rdnavcompare(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 
432 static KMF_RETURN
ParseDistinguishedName(char * buf,int len,KMF_X509_NAME * name)433 ParseDistinguishedName(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 
464 loser:
465 	kmf_free_dn(name);
466 	return (rv);
467 }
468 
469 static KMF_BOOL
IsEqualData(KMF_DATA * d1,KMF_DATA * d2)470 IsEqualData(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  */
484 int
kmf_compare_rdns(KMF_X509_NAME * name1,KMF_X509_NAME * name2)485 kmf_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  */
530 KMF_RETURN
kmf_dn_parser(char * string,KMF_X509_NAME * name)531 kmf_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 
542 static const char hexdigits[] = "0123456789abcdef";
543 
544 static KMF_RETURN
binvalue_to_string(KMF_DATA * data,custr_t * str)545 binvalue_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  */
572 static KMF_RETURN
value_to_string(KMF_DATA * data,custr_t * str)573 value_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  */
637 static KMF_RETURN
ava_to_string(KMF_X509_TYPE_VALUE_PAIR * tvp,custr_t * str)638 ava_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 
692 done:
693 	if (!found)
694 		free((void *)attr);
695 
696 	return (ret);
697 }
698 
699 static KMF_RETURN
rdn_to_string(KMF_X509_RDN * rdn,custr_t * str)700 rdn_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  */
722 KMF_RETURN
kmf_dn_to_string(KMF_X509_NAME * name,char ** string)723 kmf_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 
752 done:
753 	custr_free(str);
754 	return (err);
755 }
756