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