199ebb4caSwyllys /*
299ebb4caSwyllys  * The contents of this file are subject to the Mozilla Public
399ebb4caSwyllys  * License Version 1.1 (the "License"); you may not use this file
499ebb4caSwyllys  * except in compliance with the License. You may obtain a copy of
599ebb4caSwyllys  * the License at http://www.mozilla.org/MPL/
699ebb4caSwyllys  *
799ebb4caSwyllys  * Software distributed under the License is distributed on an "AS
899ebb4caSwyllys  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
999ebb4caSwyllys  * implied. See the License for the specific language governing
1099ebb4caSwyllys  * rights and limitations under the License.
1199ebb4caSwyllys  *
1299ebb4caSwyllys  * The Original Code is the Netscape security libraries.
1399ebb4caSwyllys  *
1499ebb4caSwyllys  * The Initial Developer of the Original Code is Netscape
1599ebb4caSwyllys  * Communications Corporation.  Portions created by Netscape are
1699ebb4caSwyllys  * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
1799ebb4caSwyllys  * Rights Reserved.
1899ebb4caSwyllys  *
1999ebb4caSwyllys  * Contributor(s):
2099ebb4caSwyllys  *
2199ebb4caSwyllys  * Alternatively, the contents of this file may be used under the
2299ebb4caSwyllys  * terms of the GNU General Public License Version 2 or later (the
2399ebb4caSwyllys  * "GPL"), in which case the provisions of the GPL are applicable
2499ebb4caSwyllys  * instead of those above.  If you wish to allow use of your
2599ebb4caSwyllys  * version of this file only under the terms of the GPL and not to
2699ebb4caSwyllys  * allow others to use your version of this file under the MPL,
2799ebb4caSwyllys  * indicate your decision by deleting the provisions above and
2899ebb4caSwyllys  * replace them with the notice and other provisions required by
2999ebb4caSwyllys  * the GPL.  If you do not delete the provisions above, a recipient
3099ebb4caSwyllys  * may use your version of this file under either the MPL or the
3199ebb4caSwyllys  * GPL.
329f0bc604SWyllys Ingersoll  *
339f0bc604SWyllys Ingersoll  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
3499ebb4caSwyllys  * Use is subject to license terms.
3599ebb4caSwyllys  *
36*bdc560abSJason King  * Copyright 2018, Joyent, Inc.
37*bdc560abSJason King  *
3899ebb4caSwyllys  * File: rdn_parser.c
3999ebb4caSwyllys  */
4099ebb4caSwyllys 
4199ebb4caSwyllys #include <strings.h>
4299ebb4caSwyllys #include <stdlib.h>
4399ebb4caSwyllys #include <kmfapi.h>
4499ebb4caSwyllys #include <kmfapiP.h>
4599ebb4caSwyllys #include <ber_der.h>
4699ebb4caSwyllys #include <rdn_parser.h>
4799ebb4caSwyllys #include <stdio.h>
4899ebb4caSwyllys #include <values.h>
49*bdc560abSJason King #include <libcustr.h>
5099ebb4caSwyllys 
5199ebb4caSwyllys /*
5299ebb4caSwyllys  * The order here is important.  The OIDs are arranged in order of
5399ebb4caSwyllys  * significance.  The CN is the most specific value, the C (country)
5499ebb4caSwyllys  * is less specific, etc.  Add to this list with care.
5599ebb4caSwyllys  */
5699ebb4caSwyllys static const struct NameToKind name2kinds[] = {
5799ebb4caSwyllys { "CN",		OID_AVA_COMMON_NAME,	(KMF_OID *)&KMFOID_CommonName},
5899ebb4caSwyllys { "SN",		OID_AVA_SURNAME,	(KMF_OID *)&KMFOID_Surname},
5999ebb4caSwyllys { "GN",		OID_AVA_GIVEN_NAME,	(KMF_OID *)&KMFOID_GivenName},
6099ebb4caSwyllys { "emailAddress", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
6199ebb4caSwyllys { "E",		OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
6299ebb4caSwyllys { "MAIL",	OID_RFC1274_MAIL,	(KMF_OID *)&KMFOID_RFC822mailbox},
6399ebb4caSwyllys { "STREET",	OID_AVA_STREET_ADDRESS, (KMF_OID *)&KMFOID_StreetAddress},
6499ebb4caSwyllys { "UID",	OID_RFC1274_UID,	(KMF_OID *)&KMFOID_userid},
6599ebb4caSwyllys { "OU",		OID_AVA_ORGANIZATIONAL_UNIT_NAME,
6699ebb4caSwyllys 			(KMF_OID *)&KMFOID_OrganizationalUnitName},
6799ebb4caSwyllys { "O",		OID_AVA_ORGANIZATION_NAME, (KMF_OID *)&KMFOID_OrganizationName},
6899ebb4caSwyllys { "L",		OID_AVA_LOCALITY,	(KMF_OID *)&KMFOID_LocalityName},
6999ebb4caSwyllys { "ST",		OID_AVA_STATE_OR_PROVINCE,
7099ebb4caSwyllys 	(KMF_OID *)&KMFOID_StateProvinceName},
7199ebb4caSwyllys { "C",		OID_AVA_COUNTRY_NAME,	(KMF_OID *)&KMFOID_CountryName},
7299ebb4caSwyllys { "DC",		OID_AVA_DC,		(KMF_OID *)&KMFOID_domainComponent},
7399ebb4caSwyllys { 0,		OID_UNKNOWN, NULL}
7499ebb4caSwyllys };
7599ebb4caSwyllys 
7699ebb4caSwyllys static KMF_BOOL
IsPrintable(unsigned char * data,unsigned len)7799ebb4caSwyllys IsPrintable(unsigned char *data, unsigned len)
7899ebb4caSwyllys {
7999ebb4caSwyllys 	unsigned char ch, *end;
8099ebb4caSwyllys 
8199ebb4caSwyllys 	end = data + len;
8299ebb4caSwyllys 	while (data < end) {
8399ebb4caSwyllys 		ch = *data++;
8499ebb4caSwyllys 		if (!IS_PRINTABLE(ch)) {
8599ebb4caSwyllys 			return (B_FALSE);
8699ebb4caSwyllys 		}
8799ebb4caSwyllys 	}
8899ebb4caSwyllys 	return (B_TRUE);
8999ebb4caSwyllys }
9099ebb4caSwyllys 
9199ebb4caSwyllys static KMF_BOOL
Is7Bit(unsigned char * data,unsigned len)9299ebb4caSwyllys Is7Bit(unsigned char *data, unsigned len)
9399ebb4caSwyllys {
9499ebb4caSwyllys 	unsigned char ch, *end;
9599ebb4caSwyllys 
9699ebb4caSwyllys 	end = data + len;
9799ebb4caSwyllys 	while (data < end) {
9899ebb4caSwyllys 		ch = *data++;
9999ebb4caSwyllys 		if ((ch & 0x80)) {
10099ebb4caSwyllys 			return (B_FALSE);
10199ebb4caSwyllys 		}
10299ebb4caSwyllys 	}
10399ebb4caSwyllys 	return (B_TRUE);
10499ebb4caSwyllys }
10599ebb4caSwyllys 
10699ebb4caSwyllys static void
skipSpace(char ** pbp,char * endptr)10799ebb4caSwyllys skipSpace(char **pbp, char *endptr)
10899ebb4caSwyllys {
10999ebb4caSwyllys 	char *bp = *pbp;
11099ebb4caSwyllys 	while (bp < endptr && OPTIONAL_SPACE(*bp)) {
11199ebb4caSwyllys 		bp++;
11299ebb4caSwyllys 	}
11399ebb4caSwyllys 	*pbp = bp;
11499ebb4caSwyllys }
11599ebb4caSwyllys 
11699ebb4caSwyllys static KMF_RETURN
scanTag(char ** pbp,char * endptr,char * tagBuf,int tagBufSize)11799ebb4caSwyllys scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize)
11899ebb4caSwyllys {
11999ebb4caSwyllys 	char *bp, *tagBufp;
12099ebb4caSwyllys 	int taglen;
12199ebb4caSwyllys 
12299ebb4caSwyllys 	if (tagBufSize <= 0)
12399ebb4caSwyllys 		return (KMF_ERR_INTERNAL);
12499ebb4caSwyllys 
12599ebb4caSwyllys 	/* skip optional leading space */
12699ebb4caSwyllys 	skipSpace(pbp, endptr);
12799ebb4caSwyllys 	if (*pbp == endptr) {
12899ebb4caSwyllys 		/* nothing left */
12999ebb4caSwyllys 		return (KMF_ERR_RDN_PARSER);
13099ebb4caSwyllys 	}
13199ebb4caSwyllys 
13299ebb4caSwyllys 	/* fill tagBuf */
13399ebb4caSwyllys 	taglen = 0;
13499ebb4caSwyllys 	bp = *pbp;
13599ebb4caSwyllys 	tagBufp = tagBuf;
13699ebb4caSwyllys 	while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
13799ebb4caSwyllys 		if (++taglen >= tagBufSize) {
13899ebb4caSwyllys 			*pbp = bp;
13999ebb4caSwyllys 			return (KMF_ERR_RDN_PARSER);
14099ebb4caSwyllys 		}
14199ebb4caSwyllys 		*tagBufp++ = *bp++;
14299ebb4caSwyllys 	}
14399ebb4caSwyllys 	/* null-terminate tagBuf -- guaranteed at least one space left */
14499ebb4caSwyllys 	*tagBufp++ = 0;
14599ebb4caSwyllys 	*pbp = bp;
14699ebb4caSwyllys 
14799ebb4caSwyllys 	/*
14899ebb4caSwyllys 	 * skip trailing spaces till we hit something - should be
14999ebb4caSwyllys 	 * an equal sign
15099ebb4caSwyllys 	 */
15199ebb4caSwyllys 	skipSpace(pbp, endptr);
15299ebb4caSwyllys 	if (*pbp == endptr) {
15399ebb4caSwyllys 		/* nothing left */
15499ebb4caSwyllys 		return (KMF_ERR_RDN_PARSER);
15599ebb4caSwyllys 	}
15699ebb4caSwyllys 	if (**pbp != C_EQUAL) {
15799ebb4caSwyllys 		/* should be an equal sign */
15899ebb4caSwyllys 		return (KMF_ERR_RDN_PARSER);
15999ebb4caSwyllys 	}
16099ebb4caSwyllys 	/* skip over the equal sign */
16199ebb4caSwyllys 	(*pbp)++;
16299ebb4caSwyllys 
16399ebb4caSwyllys 	return (KMF_OK);
16499ebb4caSwyllys }
16599ebb4caSwyllys 
16699ebb4caSwyllys static KMF_RETURN
scanVal(char ** pbp,char * endptr,char * valBuf,int valBufSize)16799ebb4caSwyllys scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize)
16899ebb4caSwyllys {
16999ebb4caSwyllys 	char *bp, *valBufp;
17099ebb4caSwyllys 	int vallen;
17199ebb4caSwyllys 	boolean_t isQuoted;
17299ebb4caSwyllys 
17399ebb4caSwyllys 	if (valBufSize <= 0)
17499ebb4caSwyllys 		return (KMF_ERR_INTERNAL);
17599ebb4caSwyllys 
17699ebb4caSwyllys 	/* skip optional leading space */
17799ebb4caSwyllys 	skipSpace(pbp, endptr);
17899ebb4caSwyllys 	if (*pbp == endptr) {
17999ebb4caSwyllys 		/* nothing left */
18099ebb4caSwyllys 		return (KMF_ERR_RDN_PARSER);
18199ebb4caSwyllys 	}
18299ebb4caSwyllys 
18399ebb4caSwyllys 	bp = *pbp;
18499ebb4caSwyllys 
18599ebb4caSwyllys 	/* quoted? */
18699ebb4caSwyllys 	if (*bp == C_DOUBLE_QUOTE) {
18799ebb4caSwyllys 		isQuoted = B_TRUE;
18899ebb4caSwyllys 		/* skip over it */
18999ebb4caSwyllys 		bp++;
19099ebb4caSwyllys 	} else {
19199ebb4caSwyllys 		isQuoted = B_FALSE;
19299ebb4caSwyllys 	}
19399ebb4caSwyllys 
19499ebb4caSwyllys 	valBufp = valBuf;
19599ebb4caSwyllys 	vallen = 0;
19699ebb4caSwyllys 	while (bp < endptr) {
19799ebb4caSwyllys 		char c = *bp;
19899ebb4caSwyllys 		if (c == C_BACKSLASH) {
19999ebb4caSwyllys 			/* escape character */
20099ebb4caSwyllys 			bp++;
20199ebb4caSwyllys 			if (bp >= endptr) {
20299ebb4caSwyllys 				/*
20399ebb4caSwyllys 				 * escape charater must appear with paired char
20499ebb4caSwyllys 				 */
20599ebb4caSwyllys 				*pbp = bp;
20699ebb4caSwyllys 				return (KMF_ERR_RDN_PARSER);
20799ebb4caSwyllys 			}
20899ebb4caSwyllys 		} else if (!isQuoted && SPECIAL_CHAR(c)) {
20999ebb4caSwyllys 			/* unescaped special and not within quoted value */
21099ebb4caSwyllys 			break;
21199ebb4caSwyllys 		} else if (c == C_DOUBLE_QUOTE) {
21299ebb4caSwyllys 			/* reached unescaped double quote */
21399ebb4caSwyllys 			break;
21499ebb4caSwyllys 		}
21599ebb4caSwyllys 		/* append character */
21699ebb4caSwyllys 		vallen++;
21799ebb4caSwyllys 		if (vallen >= valBufSize) {
21899ebb4caSwyllys 			*pbp = bp;
21999ebb4caSwyllys 			return (KMF_ERR_RDN_PARSER);
22099ebb4caSwyllys 		}
22199ebb4caSwyllys 		*valBufp++ = *bp++;
22299ebb4caSwyllys 	}
22399ebb4caSwyllys 
22499ebb4caSwyllys 	/* stip trailing spaces from unquoted values */
22599ebb4caSwyllys 	if (!isQuoted) {
22699ebb4caSwyllys 		if (valBufp > valBuf) {
22799ebb4caSwyllys 			valBufp--;
22899ebb4caSwyllys 			while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) {
22999ebb4caSwyllys 				valBufp--;
23099ebb4caSwyllys 			}
23199ebb4caSwyllys 			valBufp++;
23299ebb4caSwyllys 		}
23399ebb4caSwyllys 	}
23499ebb4caSwyllys 
23599ebb4caSwyllys 	if (isQuoted) {
23699ebb4caSwyllys 		/* insist that we stopped on a double quote */
23799ebb4caSwyllys 		if (*bp != C_DOUBLE_QUOTE) {
23899ebb4caSwyllys 			*pbp = bp;
23999ebb4caSwyllys 			return (KMF_ERR_RDN_PARSER);
24099ebb4caSwyllys 		}
24199ebb4caSwyllys 		/* skip over the quote and skip optional space */
24299ebb4caSwyllys 		bp++;
24399ebb4caSwyllys 		skipSpace(&bp, endptr);
24499ebb4caSwyllys 	}
24599ebb4caSwyllys 
24699ebb4caSwyllys 	*pbp = bp;
24799ebb4caSwyllys 
24899ebb4caSwyllys 	if (valBufp == valBuf) {
24999ebb4caSwyllys 		/* empty value -- not allowed */
25099ebb4caSwyllys 		return (KMF_ERR_RDN_PARSER);
25199ebb4caSwyllys 	}
25299ebb4caSwyllys 
25399ebb4caSwyllys 	/* null-terminate valBuf -- guaranteed at least one space left */
25499ebb4caSwyllys 	*valBufp++ = 0;
25599ebb4caSwyllys 
25699ebb4caSwyllys 	return (KMF_OK);
25799ebb4caSwyllys }
25899ebb4caSwyllys 
25999ebb4caSwyllys static KMF_RETURN
CreateRDN(KMF_X509_TYPE_VALUE_PAIR * ava,KMF_X509_RDN * newrdn)26099ebb4caSwyllys CreateRDN(KMF_X509_TYPE_VALUE_PAIR *ava, KMF_X509_RDN *newrdn)
26199ebb4caSwyllys {
26299ebb4caSwyllys 	/* Each RDN has 1 AttrTypeAndValue */
26399ebb4caSwyllys 	(void) memset(newrdn, 0, sizeof (KMF_X509_RDN));
26499ebb4caSwyllys 	newrdn->numberOfPairs = 1;
26599ebb4caSwyllys 	newrdn->AttributeTypeAndValue = ava;
26699ebb4caSwyllys 
26799ebb4caSwyllys 	return (KMF_OK);
26899ebb4caSwyllys }
26999ebb4caSwyllys 
27099ebb4caSwyllys static KMF_RETURN
copy_oid(KMF_OID * dst,KMF_OID * src)27199ebb4caSwyllys copy_oid(KMF_OID *dst, KMF_OID *src)
27299ebb4caSwyllys {
27399ebb4caSwyllys 	KMF_RETURN ret = KMF_OK;
27499ebb4caSwyllys 
27599ebb4caSwyllys 	if (dst == NULL || src == NULL)
27699ebb4caSwyllys 		return (KMF_ERR_BAD_PARAMETER);
27799ebb4caSwyllys 
27899ebb4caSwyllys 	dst->Data = malloc(src->Length);
27999ebb4caSwyllys 	if (dst->Data == NULL)
28099ebb4caSwyllys 		return (KMF_ERR_MEMORY);
28199ebb4caSwyllys 
28299ebb4caSwyllys 	dst->Length = src->Length;
28399ebb4caSwyllys 	(void) memcpy(dst->Data, src->Data, src->Length);
28499ebb4caSwyllys 
28599ebb4caSwyllys 	return (ret);
28699ebb4caSwyllys }
28799ebb4caSwyllys 
28899ebb4caSwyllys static KMF_RETURN
CreateAVA(KMF_OID * oid,int valueType,char * value,KMF_X509_TYPE_VALUE_PAIR ** newava)28999ebb4caSwyllys CreateAVA(KMF_OID *oid, int valueType, char *value,
29099ebb4caSwyllys     KMF_X509_TYPE_VALUE_PAIR **newava)
29199ebb4caSwyllys {
29299ebb4caSwyllys 	int rv = KMF_OK;
29399ebb4caSwyllys 	KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
29499ebb4caSwyllys 
29599ebb4caSwyllys 	*newava = NULL;
29699ebb4caSwyllys 	ava = (KMF_X509_TYPE_VALUE_PAIR*) malloc(
29799ebb4caSwyllys 	    sizeof (KMF_X509_TYPE_VALUE_PAIR));
29899ebb4caSwyllys 	if (ava == NULL) {
29999ebb4caSwyllys 		return (KMF_ERR_MEMORY);
30099ebb4caSwyllys 	} else {
30199ebb4caSwyllys 		(void) memset(ava, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR));
30299ebb4caSwyllys 		ava->valueType = valueType;
30399ebb4caSwyllys 		ava->value.Data = malloc(strlen(value));
30499ebb4caSwyllys 		if (ava->value.Data == NULL) {
30599ebb4caSwyllys 			free(ava);
30699ebb4caSwyllys 			return (KMF_ERR_MEMORY);
30799ebb4caSwyllys 		}
30899ebb4caSwyllys 		(void) memcpy(ava->value.Data, value, strlen(value));
30999ebb4caSwyllys 		ava->value.Length = strlen(value);
31099ebb4caSwyllys 
31199ebb4caSwyllys 		rv = copy_oid(&ava->type, oid);
31299ebb4caSwyllys 		if (rv != KMF_OK) {
31399ebb4caSwyllys 			/* Illegal AVA type */
31499ebb4caSwyllys 			free(ava->value.Data);
31599ebb4caSwyllys 			free(ava);
31699ebb4caSwyllys 			return (rv);
31799ebb4caSwyllys 		}
31899ebb4caSwyllys 	}
31999ebb4caSwyllys 	*newava = ava;
32099ebb4caSwyllys 
32199ebb4caSwyllys 	return (rv);
32299ebb4caSwyllys }
32399ebb4caSwyllys 
32499ebb4caSwyllys static KMF_RETURN
ParseRdnAttribute(char ** pbp,char * endptr,boolean_t singleAVA,KMF_X509_TYPE_VALUE_PAIR ** a)32599ebb4caSwyllys ParseRdnAttribute(char **pbp, char *endptr, boolean_t singleAVA,
32699ebb4caSwyllys     KMF_X509_TYPE_VALUE_PAIR **a)
32799ebb4caSwyllys {
32899ebb4caSwyllys 	KMF_RETURN rv;
32999ebb4caSwyllys 	const struct NameToKind *n2k;
33099ebb4caSwyllys 	int vt;
33199ebb4caSwyllys 	int valLen;
33299ebb4caSwyllys 	char *bp;
33399ebb4caSwyllys 
33499ebb4caSwyllys 	char tagBuf[32];
33599ebb4caSwyllys 	char valBuf[384];
33699ebb4caSwyllys 
33799ebb4caSwyllys 	rv = scanTag(pbp, endptr, tagBuf, sizeof (tagBuf));
33899ebb4caSwyllys 	if (rv != KMF_OK)
33999ebb4caSwyllys 		return (rv);
34099ebb4caSwyllys 	rv = scanVal(pbp, endptr, valBuf, sizeof (valBuf));
34199ebb4caSwyllys 	if (rv != KMF_OK)
34299ebb4caSwyllys 		return (rv);
34399ebb4caSwyllys 
34499ebb4caSwyllys 	/* insist that if we haven't finished we've stopped on a separator */
34599ebb4caSwyllys 	bp = *pbp;
34699ebb4caSwyllys 	if (bp < endptr) {
34799ebb4caSwyllys 		if (singleAVA || (*bp != ',' && *bp != ';')) {
34899ebb4caSwyllys 			*pbp = bp;
34999ebb4caSwyllys 			return (KMF_ERR_RDN_ATTR);
35099ebb4caSwyllys 		}
35199ebb4caSwyllys 		/* ok, skip over separator */
35299ebb4caSwyllys 		bp++;
35399ebb4caSwyllys 	}
35499ebb4caSwyllys 	*pbp = bp;
35599ebb4caSwyllys 
35699ebb4caSwyllys 	for (n2k = name2kinds; n2k->name; n2k++) {
35799ebb4caSwyllys 		if (strcasecmp(n2k->name, tagBuf) == 0) {
35899ebb4caSwyllys 			valLen = strlen(valBuf);
35999ebb4caSwyllys 			if (n2k->kind == OID_AVA_COUNTRY_NAME) {
36099ebb4caSwyllys 				vt = BER_PRINTABLE_STRING;
36199ebb4caSwyllys 				if (valLen != 2) {
36299ebb4caSwyllys 					return (KMF_ERR_RDN_ATTR);
36399ebb4caSwyllys 				}
36499ebb4caSwyllys 				if (!IsPrintable((unsigned char *) valBuf, 2)) {
36599ebb4caSwyllys 					return (KMF_ERR_RDN_ATTR);
36699ebb4caSwyllys 				}
36799ebb4caSwyllys 			} else if ((n2k->kind == OID_PKCS9_EMAIL_ADDRESS) ||
36830a5e8faSwyllys 			    (n2k->kind == OID_RFC1274_MAIL)) {
36999ebb4caSwyllys 				vt = BER_IA5STRING;
37099ebb4caSwyllys 			} else {
37199ebb4caSwyllys 				/*
37299ebb4caSwyllys 				 * Hack -- for rationale see X.520
37399ebb4caSwyllys 				 * DirectoryString defn
37499ebb4caSwyllys 				 */
37599ebb4caSwyllys 				if (IsPrintable((unsigned char *)valBuf,
37699ebb4caSwyllys 				    valLen)) {
37799ebb4caSwyllys 					vt = BER_PRINTABLE_STRING;
37899ebb4caSwyllys 				} else if (Is7Bit((unsigned char *)valBuf,
37999ebb4caSwyllys 				    valLen)) {
38099ebb4caSwyllys 					vt = BER_T61STRING;
38199ebb4caSwyllys 				}
38299ebb4caSwyllys 			}
38330a5e8faSwyllys 			rv = CreateAVA(n2k->OID, vt, (char *)valBuf, a);
38499ebb4caSwyllys 			return (rv);
38599ebb4caSwyllys 		}
38699ebb4caSwyllys 	}
38799ebb4caSwyllys 	/* matched no kind -- invalid tag */
38899ebb4caSwyllys 	return (KMF_ERR_RDN_ATTR);
38999ebb4caSwyllys }
39099ebb4caSwyllys 
39199ebb4caSwyllys static int
rdnavcompare(const void * a,const void * b)39299ebb4caSwyllys rdnavcompare(const void *a, const void *b)
39399ebb4caSwyllys {
39499ebb4caSwyllys 	KMF_X509_RDN *r1, *r2;
39599ebb4caSwyllys 	KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
39699ebb4caSwyllys 	int i, p1, p2;
39799ebb4caSwyllys 	const struct NameToKind *n2k;
39899ebb4caSwyllys 	KMF_OID *oidrec;
39999ebb4caSwyllys 
40099ebb4caSwyllys 	r1 = (KMF_X509_RDN *)a;
40199ebb4caSwyllys 	r2 = (KMF_X509_RDN *)b;
40299ebb4caSwyllys 
40399ebb4caSwyllys 	av1 = r1->AttributeTypeAndValue;
40499ebb4caSwyllys 	av2 = r2->AttributeTypeAndValue;
40599ebb4caSwyllys 
40699ebb4caSwyllys 	p1 = p2 = MAXINT;
40799ebb4caSwyllys 	/*
40899ebb4caSwyllys 	 * The "Name2Kinds" list is ordered by significance.
40999ebb4caSwyllys 	 * Compare the "ranking" of each of the OIDs to determine
41099ebb4caSwyllys 	 * the result.
41199ebb4caSwyllys 	 */
41299ebb4caSwyllys 	for (n2k = name2kinds, i = 0;
41330a5e8faSwyllys 	    n2k->name && (p1 == MAXINT || p2 == MAXINT);
41430a5e8faSwyllys 	    n2k++, i++) {
41599ebb4caSwyllys 		oidrec = n2k->OID;
41699ebb4caSwyllys 		if (oidrec != NULL) {
41799ebb4caSwyllys 			if (IsEqualOid(&av1->type, oidrec))
41899ebb4caSwyllys 				p1 = i;
41999ebb4caSwyllys 			if (IsEqualOid(&av2->type, oidrec))
42099ebb4caSwyllys 				p2 = i;
42199ebb4caSwyllys 		}
42299ebb4caSwyllys 	}
42399ebb4caSwyllys 
42499ebb4caSwyllys 	if (p1 > p2)
42599ebb4caSwyllys 		return (-1);
42699ebb4caSwyllys 	else if (p1 < p2)
42799ebb4caSwyllys 		return (1);
42899ebb4caSwyllys 	else  /* If equal, treat as if it is less than */
42999ebb4caSwyllys 		return (1);
43099ebb4caSwyllys }
43199ebb4caSwyllys 
43230a5e8faSwyllys static KMF_RETURN
ParseDistinguishedName(char * buf,int len,KMF_X509_NAME * name)43399ebb4caSwyllys ParseDistinguishedName(char *buf, int len, KMF_X509_NAME *name)
43499ebb4caSwyllys {
43599ebb4caSwyllys 	KMF_RETURN rv = KMF_OK;
43699ebb4caSwyllys 	char *bp, *e;
43799ebb4caSwyllys 	KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
43899ebb4caSwyllys 	KMF_X509_RDN rdn;
43999ebb4caSwyllys 
44099ebb4caSwyllys 	(void) memset(name, 0, sizeof (KMF_X509_NAME));
44199ebb4caSwyllys 	e = buf + len;
44299ebb4caSwyllys 	bp = buf;
44399ebb4caSwyllys 	while (bp < e) {
44499ebb4caSwyllys 		rv = ParseRdnAttribute(&bp, e, B_FALSE, &ava);
44599ebb4caSwyllys 		if (rv != KMF_OK) goto loser;
44699ebb4caSwyllys 		rv = CreateRDN(ava, &rdn);
44799ebb4caSwyllys 		if (rv != KMF_OK) goto loser;
44899ebb4caSwyllys 		if (AddRDN(name, &rdn) != KMF_OK) goto loser;
44999ebb4caSwyllys 		skipSpace(&bp, e);
45099ebb4caSwyllys 	}
45199ebb4caSwyllys 
45299ebb4caSwyllys 	/*
45399ebb4caSwyllys 	 * Canonicalize the DN by sorting the elements
45499ebb4caSwyllys 	 * in little-endian order, as per RFC 1485:
45599ebb4caSwyllys 	 * "The name is presented/input in a little-endian
45699ebb4caSwyllys 	 * order (most significant component last)."
45799ebb4caSwyllys 	 */
45899ebb4caSwyllys 	qsort((void *)name->RelativeDistinguishedName,
45930a5e8faSwyllys 	    name->numberOfRDNs, sizeof (KMF_X509_RDN), rdnavcompare);
46099ebb4caSwyllys 
46199ebb4caSwyllys 	/* return result */
46299ebb4caSwyllys 	return (rv);
46399ebb4caSwyllys 
46499ebb4caSwyllys loser:
46530a5e8faSwyllys 	kmf_free_dn(name);
46699ebb4caSwyllys 	return (rv);
46799ebb4caSwyllys }
46899ebb4caSwyllys 
46999ebb4caSwyllys static KMF_BOOL
IsEqualData(KMF_DATA * d1,KMF_DATA * d2)47099ebb4caSwyllys IsEqualData(KMF_DATA *d1, KMF_DATA *d2)
47199ebb4caSwyllys {
47299ebb4caSwyllys 	return ((d1->Length == d2->Length) &&
47399ebb4caSwyllys 	    !memcmp(d1->Data, d2->Data, d1->Length));
47499ebb4caSwyllys }
47599ebb4caSwyllys 
47699ebb4caSwyllys /*
47799ebb4caSwyllys  * Generic routine to compare 2 RDN structures.
47899ebb4caSwyllys  *
47999ebb4caSwyllys  * Because the ordering of the AV pairs may not be
48099ebb4caSwyllys  * the same, we must compare each AV pair individually
48199ebb4caSwyllys  *
48299ebb4caSwyllys  * Return 0 if equal, 1 if not.
48399ebb4caSwyllys  */
48499ebb4caSwyllys int
kmf_compare_rdns(KMF_X509_NAME * name1,KMF_X509_NAME * name2)48530a5e8faSwyllys kmf_compare_rdns(KMF_X509_NAME *name1, KMF_X509_NAME *name2)
48699ebb4caSwyllys {
48799ebb4caSwyllys 	int i, j;
48899ebb4caSwyllys 	boolean_t avfound;
48999ebb4caSwyllys 	KMF_X509_RDN *r1, *r2;
49099ebb4caSwyllys 	KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
49199ebb4caSwyllys 
49299ebb4caSwyllys 	if (name1 == NULL || name2 == NULL)
49399ebb4caSwyllys 		return (1);
49499ebb4caSwyllys 
49599ebb4caSwyllys 	if (name1->numberOfRDNs != name2->numberOfRDNs)
49699ebb4caSwyllys 		return (1);
49799ebb4caSwyllys 
49899ebb4caSwyllys 	for (i = 0; i < name1->numberOfRDNs; i++) {
49999ebb4caSwyllys 		r1 = (KMF_X509_RDN *)&name1->RelativeDistinguishedName[i];
50099ebb4caSwyllys 		av1 = (KMF_X509_TYPE_VALUE_PAIR *)r1->AttributeTypeAndValue;
50199ebb4caSwyllys 
50299ebb4caSwyllys 		avfound = FALSE;
50399ebb4caSwyllys 		for (j = 0; j < name2->numberOfRDNs && !avfound; j++) {
50499ebb4caSwyllys 			r2 = (KMF_X509_RDN *)
50530a5e8faSwyllys 			    &name2->RelativeDistinguishedName[j];
50699ebb4caSwyllys 			av2 = (KMF_X509_TYPE_VALUE_PAIR *)
50730a5e8faSwyllys 			    r2->AttributeTypeAndValue;
50899ebb4caSwyllys 
50999ebb4caSwyllys 			avfound = (IsEqualOid(&av1->type, &av2->type) &&
51030a5e8faSwyllys 			    IsEqualData(&av1->value, &av2->value));
51199ebb4caSwyllys 		}
51299ebb4caSwyllys 		/*
51399ebb4caSwyllys 		 * If the current AV from name1 was not found in name2,
51499ebb4caSwyllys 		 * we are done.
51599ebb4caSwyllys 		 */
51699ebb4caSwyllys 		if (!avfound)
51799ebb4caSwyllys 			return (1);
51899ebb4caSwyllys 	}
51999ebb4caSwyllys 
52099ebb4caSwyllys 	/* If we got this far, it must be a match */
52199ebb4caSwyllys 	return (0);
52299ebb4caSwyllys }
52330a5e8faSwyllys 
52430a5e8faSwyllys /*
52530a5e8faSwyllys  * kmf_dn_parser
52630a5e8faSwyllys  *
52730a5e8faSwyllys  * Public interface for parsing a Distinguished name in
52830a5e8faSwyllys  * human-readable format into a binary KMF_X509_NAME.
52930a5e8faSwyllys  */
53030a5e8faSwyllys KMF_RETURN
kmf_dn_parser(char * string,KMF_X509_NAME * name)53130a5e8faSwyllys kmf_dn_parser(char *string, KMF_X509_NAME *name)
53230a5e8faSwyllys {
53330a5e8faSwyllys 	KMF_RETURN err;
53430a5e8faSwyllys 
53530a5e8faSwyllys 	if (string == NULL || name == NULL)
53630a5e8faSwyllys 		return (KMF_ERR_BAD_PARAMETER);
53730a5e8faSwyllys 
53830a5e8faSwyllys 	err = ParseDistinguishedName(string, (int)strlen(string), name);
53930a5e8faSwyllys 	return (err);
54030a5e8faSwyllys }
541*bdc560abSJason King 
542*bdc560abSJason King static const char hexdigits[] = "0123456789abcdef";
543*bdc560abSJason King 
544*bdc560abSJason King static KMF_RETURN
binvalue_to_string(KMF_DATA * data,custr_t * str)545*bdc560abSJason King binvalue_to_string(KMF_DATA *data, custr_t *str)
546*bdc560abSJason King {
547*bdc560abSJason King 	size_t i;
548*bdc560abSJason King 	uchar_t c;
549*bdc560abSJason King 
550*bdc560abSJason King 	if (custr_appendc(str, '#') != 0)
551*bdc560abSJason King 		return (KMF_ERR_MEMORY);
552*bdc560abSJason King 
553*bdc560abSJason King 	for (i = 0; i < data->Length; i++) {
554*bdc560abSJason King 		c = data->Data[i];
555*bdc560abSJason King 		if (custr_appendc(str, hexdigits[(c >> 4) & 0xf]) != 0 ||
556*bdc560abSJason King 		    custr_appendc(str, hexdigits[(c & 0xf)]) != 0) {
557*bdc560abSJason King 			return (KMF_ERR_MEMORY);
558*bdc560abSJason King 		}
559*bdc560abSJason King 	}
560*bdc560abSJason King 
561*bdc560abSJason King 	return (KMF_OK);
562*bdc560abSJason King }
563*bdc560abSJason King 
564*bdc560abSJason King /*
565*bdc560abSJason King  * Convert an RDN value into a printable name with appropriate escaping.
566*bdc560abSJason King  * The rules are taken from RFC4514.  While it is dealing with LDAP
567*bdc560abSJason King  * distinguished names, both LDAP and x509 certificates are based on the
568*bdc560abSJason King  * same underlying ITU standards, and as far as I can determine, the same
569*bdc560abSJason King  * rules apply (or at least the rules for LDAP DNs apply the same to x509
570*bdc560abSJason King  * DNs).
571*bdc560abSJason King  */
572*bdc560abSJason King static KMF_RETURN
value_to_string(KMF_DATA * data,custr_t * str)573*bdc560abSJason King value_to_string(KMF_DATA *data, custr_t *str)
574*bdc560abSJason King {
575*bdc560abSJason King 	size_t i;
576*bdc560abSJason King 	uchar_t c;
577*bdc560abSJason King 
578*bdc560abSJason King 	for (i = 0; i < data->Length; i++) {
579*bdc560abSJason King 		c = data->Data[i];
580*bdc560abSJason King 
581*bdc560abSJason King 		/*
582*bdc560abSJason King 		 * While technically not required, it is suggested that
583*bdc560abSJason King 		 * printable non-ascii characters (e.g. multi-byte UTF-8
584*bdc560abSJason King 		 * characters) are converted as escaped hex (as well as
585*bdc560abSJason King 		 * unprintable characters).  AFAIK there is no one canonical
586*bdc560abSJason King 		 * string representation (e.g. attribute names are case
587*bdc560abSJason King 		 * insensitive, so 'CN=foo' and 'cn=foo' convert to the same
588*bdc560abSJason King 		 * binary representation, but there is nothing to say if
589*bdc560abSJason King 		 * either string form is canonical), so this shouldn't
590*bdc560abSJason King 		 * pose a problem.
591*bdc560abSJason King 		 */
592*bdc560abSJason King 		if (c < ' ' || c >= 0x7f) {
593*bdc560abSJason King 			/*
594*bdc560abSJason King 			 * RFC4514 specifies the hex form in a DN string as
595*bdc560abSJason King 			 * \{hex}{hex}. OpenSSL uses capitals for A-F so we
596*bdc560abSJason King 			 * do the same.
597*bdc560abSJason King 			 */
598*bdc560abSJason King 			if (custr_append_printf(str, "\\%02hhX", c) != 0)
599*bdc560abSJason King 				return (KMF_ERR_MEMORY);
600*bdc560abSJason King 			continue;
601*bdc560abSJason King 		}
602*bdc560abSJason King 
603*bdc560abSJason King 		switch (c) {
604*bdc560abSJason King 		case '#':
605*bdc560abSJason King 			/* Escape # if at the start of a value */
606*bdc560abSJason King 			if (i != 0)
607*bdc560abSJason King 				break;
608*bdc560abSJason King 			/* FALLTHROUGH */
609*bdc560abSJason King 		case ' ':
610*bdc560abSJason King 			/* Escape ' ' if at the start or end of a value */
611*bdc560abSJason King 			if (i != 0 && i + 1 != data->Length)
612*bdc560abSJason King 				break;
613*bdc560abSJason King 			/* FALLTHROUGH */
614*bdc560abSJason King 		case '"':
615*bdc560abSJason King 		case '+':
616*bdc560abSJason King 		case ',':
617*bdc560abSJason King 		case ';':
618*bdc560abSJason King 		case '<':
619*bdc560abSJason King 		case '>':
620*bdc560abSJason King 		case '\\':
621*bdc560abSJason King 			/* Escape these */
622*bdc560abSJason King 			if (custr_appendc(str, '\\') != 0)
623*bdc560abSJason King 				return (KMF_ERR_MEMORY);
624*bdc560abSJason King 		}
625*bdc560abSJason King 
626*bdc560abSJason King 		if (custr_appendc(str, c) != 0)
627*bdc560abSJason King 			return (KMF_ERR_MEMORY);
628*bdc560abSJason King 	}
629*bdc560abSJason King 
630*bdc560abSJason King 	return (KMF_OK);
631*bdc560abSJason King }
632*bdc560abSJason King 
633*bdc560abSJason King /*
634*bdc560abSJason King  * Translate an attribute/value pair into a string.  If the attribute OID
635*bdc560abSJason King  * is a well known OID (in name2kinds) we use the name instead of the OID.
636*bdc560abSJason King  */
637*bdc560abSJason King static KMF_RETURN
ava_to_string(KMF_X509_TYPE_VALUE_PAIR * tvp,custr_t * str)638*bdc560abSJason King ava_to_string(KMF_X509_TYPE_VALUE_PAIR *tvp, custr_t *str)
639*bdc560abSJason King {
640*bdc560abSJason King 	KMF_OID *kind_oid;
641*bdc560abSJason King 	KMF_OID *rdn_oid = &tvp->type;
642*bdc560abSJason King 	const char *attr = NULL;
643*bdc560abSJason King 	size_t i;
644*bdc560abSJason King 	KMF_RETURN ret = KMF_OK;
645*bdc560abSJason King 	boolean_t found = B_FALSE;
646*bdc560abSJason King 
647*bdc560abSJason King 	for (i = 0; name2kinds[i].name != NULL; i++) {
648*bdc560abSJason King 		kind_oid = name2kinds[i].OID;
649*bdc560abSJason King 
650*bdc560abSJason King 		if (!IsEqualOid(kind_oid, rdn_oid))
651*bdc560abSJason King 			continue;
652*bdc560abSJason King 
653*bdc560abSJason King 		attr = name2kinds[i].name;
654*bdc560abSJason King 		found = B_TRUE;
655*bdc560abSJason King 		break;
656*bdc560abSJason King 	}
657*bdc560abSJason King 
658*bdc560abSJason King 	if (!found && (attr = kmf_oid_to_string(rdn_oid)) == NULL) {
659*bdc560abSJason King 		ret = KMF_ERR_MEMORY;
660*bdc560abSJason King 		goto done;
661*bdc560abSJason King 	}
662*bdc560abSJason King 	if (custr_append(str, attr) != 0) {
663*bdc560abSJason King 		ret = KMF_ERR_MEMORY;
664*bdc560abSJason King 		goto done;
665*bdc560abSJason King 	}
666*bdc560abSJason King 	if (custr_appendc(str, '=') != 0) {
667*bdc560abSJason King 		ret = KMF_ERR_MEMORY;
668*bdc560abSJason King 		goto done;
669*bdc560abSJason King 	}
670*bdc560abSJason King 
671*bdc560abSJason King 	/*
672*bdc560abSJason King 	 * RFC4514 indicates that an oid=value pair should have the value
673*bdc560abSJason King 	 * printed as #xxxxxx.  In addition, we also print as a binary
674*bdc560abSJason King 	 * value if the BER tag does not indicate the value is some sort
675*bdc560abSJason King 	 * of printable string.
676*bdc560abSJason King 	 */
677*bdc560abSJason King 	switch (tvp->valueType) {
678*bdc560abSJason King 	case BER_UTF8_STRING:
679*bdc560abSJason King 	case BER_PRINTABLE_STRING:
680*bdc560abSJason King 	case BER_T61STRING:
681*bdc560abSJason King 	case BER_IA5STRING:
682*bdc560abSJason King 		if (found) {
683*bdc560abSJason King 			ret = value_to_string(&tvp->value, str);
684*bdc560abSJason King 			break;
685*bdc560abSJason King 		}
686*bdc560abSJason King 		/*FALLTHROUGH*/
687*bdc560abSJason King 	default:
688*bdc560abSJason King 		ret = binvalue_to_string(&tvp->value, str);
689*bdc560abSJason King 		break;
690*bdc560abSJason King 	}
691*bdc560abSJason King 
692*bdc560abSJason King done:
693*bdc560abSJason King 	if (!found)
694*bdc560abSJason King 		free((void *)attr);
695*bdc560abSJason King 
696*bdc560abSJason King 	return (ret);
697*bdc560abSJason King }
698*bdc560abSJason King 
699*bdc560abSJason King static KMF_RETURN
rdn_to_string(KMF_X509_RDN * rdn,custr_t * str)700*bdc560abSJason King rdn_to_string(KMF_X509_RDN *rdn, custr_t *str)
701*bdc560abSJason King {
702*bdc560abSJason King 	KMF_RETURN ret;
703*bdc560abSJason King 	size_t i;
704*bdc560abSJason King 
705*bdc560abSJason King 	for (i = 0; i < rdn->numberOfPairs; i++) {
706*bdc560abSJason King 		if (i > 0 && custr_appendc(str, '+') != 0)
707*bdc560abSJason King 			return (KMF_ERR_MEMORY);
708*bdc560abSJason King 
709*bdc560abSJason King 		ret = ava_to_string(&rdn->AttributeTypeAndValue[i], str);
710*bdc560abSJason King 		if (ret != KMF_OK)
711*bdc560abSJason King 			return (ret);
712*bdc560abSJason King 	}
713*bdc560abSJason King 
714*bdc560abSJason King 	return (KMF_OK);
715*bdc560abSJason King }
716*bdc560abSJason King 
717*bdc560abSJason King /*
718*bdc560abSJason King  * kmf_dn_to_string
719*bdc560abSJason King  *
720*bdc560abSJason King  * Take a binary KMF_X509_NAME and convert it into a human readable string.
721*bdc560abSJason King  */
722*bdc560abSJason King KMF_RETURN
kmf_dn_to_string(KMF_X509_NAME * name,char ** string)723*bdc560abSJason King kmf_dn_to_string(KMF_X509_NAME *name, char **string)
724*bdc560abSJason King {
725*bdc560abSJason King 	custr_t *str = NULL;
726*bdc560abSJason King 	KMF_RETURN err = KMF_OK;
727*bdc560abSJason King 	size_t i;
728*bdc560abSJason King 
729*bdc560abSJason King 	if (name == NULL || string == NULL)
730*bdc560abSJason King 		return (KMF_ERR_BAD_PARAMETER);
731*bdc560abSJason King 
732*bdc560abSJason King 	*string = NULL;
733*bdc560abSJason King 
734*bdc560abSJason King 	if (custr_alloc(&str) != 0)
735*bdc560abSJason King 		return (KMF_ERR_MEMORY);
736*bdc560abSJason King 
737*bdc560abSJason King 	for (i = 0; i < name->numberOfRDNs; i++) {
738*bdc560abSJason King 		KMF_X509_RDN *rdn = &name->RelativeDistinguishedName[i];
739*bdc560abSJason King 
740*bdc560abSJason King 		if (i > 0 && custr_append(str, ", ") != 0) {
741*bdc560abSJason King 			err = KMF_ERR_MEMORY;
742*bdc560abSJason King 			goto done;
743*bdc560abSJason King 		}
744*bdc560abSJason King 
745*bdc560abSJason King 		if ((err = rdn_to_string(rdn, str)) != KMF_OK)
746*bdc560abSJason King 			goto done;
747*bdc560abSJason King 	}
748*bdc560abSJason King 
749*bdc560abSJason King 	if ((*string = strdup(custr_cstr(str))) == NULL)
750*bdc560abSJason King 		err = KMF_ERR_MEMORY;
751*bdc560abSJason King 
752*bdc560abSJason King done:
753*bdc560abSJason King 	custr_free(str);
754*bdc560abSJason King 	return (err);
755*bdc560abSJason King }
756